标签 环境变量 下的文章

已经在虚拟机部署好Apache DolphinScheduler了,想尝试下在Flink新建一个Flink节点,然后用Flink消费Kafka数据。

Apache DolphinScheduler用的是单机部署,具体操作可以参考官方文档:DolphinScheduler | 文档中心(https://dolphinscheduler.apache.org/zh-cn/docs/3.3.2/guide/in...).

  • 前置条件:已经安装Java 11、DolphinScheduler 3.3.2、Flink 1.18.1、Kafka 3.6.0,Zookeeper用Kafka内置的。建议这些安装都下载二进制的安装包到虚拟机安装,用命令安装的不可控,我下载的二进制包如下:

配置好Flink的环境变量

1、编辑环境变量:

sudo vim ~/.bashrc

增加Flink的路径

2、使环境变量生效:

#使环境变量生效
source ~/.bashrc
#查看环境变量
echo $Flink_HOME

修改Kafka、Flink以及DolphinScheduler的配置文件

因为用的是虚拟机,为了让外面的主机能够访问到虚拟机的网络,需要修改下配置文件

  1. 修改Kafka配置:找到Kafka安装包下的config文件夹,修改config下的server.properties文件,修改listeners是为了外面的主机能够访问到虚拟机的Kafka,还有把advertised.listeners改成虚拟机地址,写样例的时候能连上虚拟机的Kafka地址,不然默认连localhost
broker.id=0
listeners=PLAINTEXT://0.0.0.0:9092
#192.168.146.132修改成虚拟机ip
advertised.listeners=PLAINTEXT://192.168.146.132:9092

  1. 修改Flink配置:找到Flink安装包下的conf文件夹,修改conf下的Flink-conf.yaml文件,把里面所有的localhost地址全部改成0.0.0.0,以便主机能访问到虚拟机的Flink。还有增加jobmanager和taskmanager的内存
jobmanager.rpc.address: 0.0.0.0
jobmanager.bind-host: 0.0.0.0
jobmanager.cpu.cores: 1
jobmanager.memory.process.size: 1600m
taskmanager.bind-host: 0.0.0.0
taskmanager.host: 0.0.0.0
taskmanager.memory.process.size: 2048m
taskmanager.cpu.cores: 1

  1. 修改Apache DolphinScheduler的配置文件,从Apache DolphinScheduler的启动脚本文件dolphinscheduler-daemon.sh可以看出,配置环境变量用的是bin/env文件夹下的dolphinscheduler_env.sh

查看dolphinscheduler-daemon.sh文件:

修改dolphinscheduler_env.sh文件,新增JAVA、Flink路径:

#修改成自己的JAVA、Flink路径
export JAVA_HOME=/data/jdk-11.0.29
export Flink_HOME=/data/Flink-1.18.1

关闭防火墙,启动应用

启动应用,包括Zookeeper、Kafka、Flink以及Apache DolphinScheduler。

#关闭防火墙
sudo systemctl stop firewalld
 
# 在 Flink 根目录下,执行以下命令启动 Flink 集群
bin/start-cluster.sh
 
# 启动 ZooKeeper
bin/zookeeper-server-start.sh config/zookeeper.properties &
 
# 启动 Kafka 服务器
bin/Kafka-server-start.sh config/server.properties &
 
#创建 Kafka 主题
bin/Kafka-topics.sh --create --topic test --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1
 
#使用命令行生产者发送消息
bin/Kafka-console-producer.sh --topic test --bootstrap-server localhost:9092
 
#消费
bin/Kafka-console-consumer.sh --topic test --from-beginning --bootstrap-server localhost:9092

# 启动 Standalone Server 服务
bash ./bin/dolphinscheduler-daemon.sh start standalone-server

测试

测试Flink、Apache DolphinScheduler是否能访问成功。

  1. Flink访问地址:http://localhost:8081/,localhost改成自己虚拟机地址

  1. Apache DolphinScheduler访问地址:http://localhost:12345/dolphinscheduler/ui ,localhost改成自己虚拟机地址即可登录系统 UI。默认的用户名和密码是 admin/dolphinscheduler123

编写样例

用Flink消费Kafka数据,然后打包上传到Apache DolphinScheduler,启动Flink任务:

  1. 编写样例:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.example</groupId>
    <artifactId>Flink-Kafka-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <Flink.version>1.18.1</Flink.version>
        <scala.binary.version>2.12</scala.binary.version>
        <Kafka.version>3.6.0</Kafka.version>
    </properties>
 
    <dependencies>
        <!-- Flink核心依赖 -->
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-java</artifactId>
            <version>${Flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-streaming-java</artifactId>
            <version>${Flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-clients</artifactId>
            <version>${Flink.version}</version>
        </dependency>
 
        <!-- 连接器基础依赖 -->
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-connector-base</artifactId>
            <version>${Flink.version}</version>
        </dependency>
 
        <!-- Kafka连接器(关键修改点) -->
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-connector-Kafka</artifactId>
            <version>3.1.0-1.18</version>
        </dependency>
        <dependency>
            <groupId>org.apache.Kafka</groupId>
            <artifactId>Kafka-clients</artifactId>
            <version>${Kafka.version}</version>
        </dependency>
 
        <!-- 日志依赖 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.36</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
 
    <repositories>
        <repository>
            <id>aliyun</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>apache-releases</id>
            <url>https://repository.apache.org/content/repositories/releases/</url>
        </repository>
    </repositories>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <excludes>
                                    <exclude>org.apache.Flink:force-shading</exclude>
                                    <exclude>com.google.code.findbugs:jsr305</exclude>
                                    <exclude>org.slf4j:*</exclude>
                                </excludes>
                            </artifactSet>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

FlinkKafkaConsumerExample.java

import org.apache.Flink.api.common.functions.FlatMapFunction;
import org.apache.Flink.api.java.tuple.Tuple2;
import org.apache.Flink.api.java.utils.ParameterTool;
import org.apache.Flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.Flink.streaming.api.datastream.DataStream;
import org.apache.Flink.streaming.api.functions.ProcessFunction;
import org.apache.Flink.streaming.api.functions.sink.RichSinkFunction;
import org.apache.Flink.util.Collector;
import org.apache.Flink.streaming.connectors.Kafka.FlinkKafkaConsumer;
import org.apache.Flink.api.common.serialization.SimpleStringSchema;
import org.apache.Kafka.clients.consumer.ConsumerConfig;
import org.apache.Kafka.common.serialization.StringDeserializer;
 
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
 
 
public class FlinkKafkaConsumerExample {
    private static volatile int messageCount = 0;
    private static volatile boolean shouldStop = false;
    public static void main(String[] args) throws Exception {
        // 设置执行环境
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
 
        // Kafka 配置
        Properties properties = new Properties();
        properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.146.132:9092"); // Kafka broker 地址
        properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "test-group"); // 消费者组
        properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
 
        // 创建 Kafka 消费者
        FlinkKafkaConsumer<String> KafkaConsumer = new FlinkKafkaConsumer<>("test", new SimpleStringSchema(), properties);
        KafkaConsumer.setStartFromEarliest(); // 从最早的消息开始消费
        DataStream<String> stream = env.addSource(KafkaConsumer);
 
        // 处理数据:分词和计数
        DataStream<Tuple2<String, Integer>> counts = stream
                .flatMap(new Tokenizer())
                .keyBy(value -> value.f0)
                .sum(1);
 
 
        counts.addSink(new RichSinkFunction<Tuple2<String, Integer>>() {
            @Override
            public void invoke(Tuple2<String, Integer> value, Context context) {
                System.out.println(value);
                messageCount++;
 
                // 检查是否达到停止条件
                if (messageCount >= 2 && !shouldStop) {
                    System.out.println("Processed 2 messages, stopping job.");
                    shouldStop = true; // 设置标志位,表示应该停止
                }
            }
        });
 
        // 执行作业并获取 JobClient
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try {
                // 启动作业并获取 JobClient
                org.apache.Flink.core.execution.JobClient jobClient = env.executeAsync("Flink Kafka WordCount");
                System.out.println("Job ID: " + jobClient.getJobID());
 
                // 监测条件并取消作业
                while (!shouldStop) {
                    Thread.sleep(100); // 每100毫秒检查一次
                }
 
                // 达到停止条件时取消作业
                if (shouldStop) {
                    System.out.println("Cancelling the job...");
                    jobClient.cancel().get(); // 取消作业
                }
 
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
 
        // 在主线程中等待作业结束
        future.join(); // 等待作业完成
    }
 
    // Tokenizer 类用于将输入字符串转化为单词
    public static final class Tokenizer implements FlatMapFunction<String, Tuple2<String, Integer>> {
        @Override
        public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
            String[] tokens = value.toLowerCase().split("\\W+");
            for (String token : tokens) {
                if (token.length() > 0) {
                    out.collect(new Tuple2<>(token, 1));
                }
            }
        }
    }
 
}
  1. 打包上传到Apache DolphinScheduler

  1. 新建Flink节点,并启动

在Apache DolphinScheduler的任务实例看启动日志:

在虚拟机启动生产者,输出字符串,然后可以在Flink查看输出Kafka生产的消息:

原文链接:https://blog.csdn.net/Analyze_ing/article/details/156940553

最近把代理服务器 IP 换了下,从 192.168.0.13 换成了 192.168.0.170,在远端 Linux 上用 Codex CLI 走新服务器的 HTTP 代理,遇到报错:

stream disconnected before completion: error sending request for url (https://chatgpt.com/backend-api/codex/responses)

export RUST_LOG=trace 打开 trace 级别日志后,查看 ~/.codex/log/codex-tui.log,发现它竟然去连旧代理 192.168.0.13:7890,并报 Host is unreachable

最诡异的是:我检查运行中的 codex 进程环境变量,里面完全看不到旧代理。
这里我采用了原来帖子里处理 codex VSCode 插件的方法直接添加了代理所以看不到原来的旧代理:

#!/usr/bin/env bash set -euo pipefail

unset http_proxy https_proxy all_proxy no_proxy
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY

PROXY="http://192.168.0.170:7890" export http_proxy="$PROXY" export https_proxy="$PROXY" export all_proxy="$PROXY" export HTTP_PROXY="$PROXY" export HTTPS_PROXY="$PROXY" export ALL_PROXY="$PROXY" export NO_PROXY="localhost,127.0.0.1,::1" export no_proxy="$NO_PROXY" # 用于确认 VS Code/CLI 实际有没有跑到这个 wrapper env | grep -i _proxy > "/tmp/codex-proxy-env.$$.txt"

HERE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" exec "$HERE/codex.real" "$@" 

1)我怎么确认 “进程 environ 里没有旧代理”

我用下面命令抓 codex 相关进程的环境变量:

查看 codex 进程对应的环境变量

for p in $(pgrep -af codex | awk '{print $1}'); do echo "=== PID $p exe=$(readlink -f /proc/$p/exe 2>/dev/null) ===" tr '\0' '\n' < /proc/$p/environ 2>/dev/null | grep -iE '(^|_)proxy=' || true done 

输出看起来代理都是统一的(只有新代理 192.168.0.170):

=== PID 1703340 exe=/home/user/.vscode-server/extensions/openai.chatgpt-0.4.62-linux-arm64/bin/linux-aarch64/codex.real ===
HTTPS_PROXY=http://192.168.0.170:7890
HTTP_PROXY=http://192.168.0.170:7890
=== PID 1713620 exe=/home/user/.local/node/bin/node ===
no_proxy=localhost,127.0.0.1,::1
https_proxy=http://192.168.0.170:7890
NO_PROXY=localhost,127.0.0.1,::1
HTTPS_PROXY=http://192.168.0.170:7890
HTTP_PROXY=http://192.168.0.170:7890
http_proxy=http://192.168.0.170:7890
ALL_PROXY=http://192.168.0.170:7890
all_proxy=http://192.168.0.170:7890
=== PID 1713631 exe=/home/user/.local/node/lib/node_modules/@openai/codex/vendor/aarch64-unknown-linux-musl/codex/codex ===
no_proxy=localhost,127.0.0.1,::1
https_proxy=http://192.168.0.170:7890
NO_PROXY=localhost,127.0.0.1,::1
HTTPS_PROXY=http://192.168.0.170:7890
HTTP_PROXY=http://192.168.0.170:7890
http_proxy=http://192.168.0.170:7890
ALL_PROXY=http://192.168.0.170:7890
all_proxy=http://192.168.0.170:7890

2) 但 trace 日志里仍然出现旧代理

我开启 trace:

追踪 codex 执行的日志

export RUST_LOG=trace
codex

然后在~/.codex/log/codex-tui.log 里看到它尝试连接旧代理 192.168.0.13:7890(典型是 reqwest /hyper 的 connect error)。

3) 最终根因:~/.codex/.env 里残留了旧代理

看了下常规的~/.bashrc , /etc/bash.bashrc 都没有老代理的影子,使用才最残暴的 sudo grep -nri 192.168.0.13 /* 暴力搜索所有文件夹下包含老代理的 IP。最后发现我以前在~/.codex/.env 里写过旧的 http_proxy/https_proxy,后来忘了删。。。。删除后,问题解决。

4) 经验总结

只看 /proc/$PID/environ 可能会被误导:即便进程环境里已经是新代理,codex-tui.log 仍可能暴露 “.env 残留的旧代理”。这时候一定要善用 export RUST_LOG=trace 来观察 codex 运行日志。


📌 转载信息
原作者:
LeBronGanDalf
转载时间:
2026/1/24 06:56:01

今天使用 https://linux.do/t/topic/1440210 佬分享的方法测试的时候,claude code 调用子代理一直返回 500 错误。

后面发现是我早上把 claude code 从 npm 安装换成了原生安装导致的。claude 官网给出的答复是:

如果搜索工具、**@file** 提及、自定义代理和自定义技能不起作用,請安裝 systemripgrep:

macOS (Homebrew)

brew install ripgrep

Windows (winget)

winget install BurntSushi.ripgrep.MSVC

Ubuntu/Debian

sudo apt install ripgrep

Alpine Linux

apk add ripgrep

Arch Linux

pacman -S ripgrep

然后在您的环境中设置 **USE_BUILTIN_RIPGREP=0**。


📌 转载信息
原作者:
hexsen
转载时间:
2026/1/23 19:24:14

点赞 + 关注 + 收藏 = 学会了

整理了一个n8n小专栏,有兴趣的工友可以关注一下 👉 《n8n修炼手册》

不管是在电脑还是 NAS 通过 Docker 部署 n8n,环境变量没配置好的话,使用 Read/Write Files from Disk 节点「读取本地本地」或者「保存文件到本地」,有可能出现这个报错。

这是 Docker + n8n 文件系统权限/路径隔离 的经典问题,不是 n8n 节点用错,而是容器只能访问被允许的目录

⚠️⚠️⚠️

想解决这个问题,首先要将你 n8n 上已有的工作流等数据找个地方保存好。因为要改环境变量,有可能会丢失数据。

⚠️⚠️⚠️

在电脑用 Docker 部署

打开 Docker,首先要在 Containers 里删掉部署好的 n8n。

然后到 Images,假设你没删掉 n8n 镜像的话,重新点击一下运行按钮。

删掉镜像了就重新拉一遍吧。可以参考《『n8n』环境搭建》

点击运行按钮后,需要添加在 Volumes 里添加一项(下图红框)。

在你的电脑,找个位置创建要给文件夹。

  • 上图红框的 Host path 这项就填入你在电脑创建的文件夹的绝对路径。
  • Container path 这项填入 /home/node/.n8n-files,必须是这个值!一个字一个符号都不能少!

然后点击“Run”按钮(弹窗右下角蓝色底色那个按钮)。

之后再浏览器输入 localhost:5678 就能运行 n8n 了。

接下来使用 Read/Write Files from Disk 节点读写文件,都是指向你刚刚在电脑创建的那个文件夹。

比如我的 /home/node/.n8n-files 指向了 文稿/n8n-data 这个文件夹,里面有一个 hello.txt 文件。

在 n8n 里使用 Read/Write Files from Disk 节点时,File(s) Selector 项需要这么写:

/home/node/.n8n-files/hello.txt

可以看到文件读取成功了。

记住记住!用法是这样的,别问为什么⬇️⬇️⬇️

/home/node/.n8n-files/文件名.后缀

在绿联 NAS 部署

如果你是在 NAS 上部署 n8n,通常使用 Docker 部署的吧~

不管你是用群晖还是其他牌子的NAS,如果使用新建项目,用是 yaml 拉镜像。

services:
  n8n:
    image: n8nio/n8n:latest   # 为了汉化成功,这里需要指定镜像版本号
    container_name: n8n
    ports:
      - 5678:5678
    volumes:
      - n8n:/home/node/.n8n # 冒号前面映射n8n文件夹绝对路径
      - n8n-files:/home/node/.n8n-files # 冒号前面映射n8n-files文件夹绝对路径
    restart: unless-stopped

那么 yaml 的代码必须在 volumes 里加一项 - n8n-files:/home/node/.n8n-files。冒号前面的 n8n-files 是允许 n8n 读写文件的文件夹的绝对路径

如果你是使用《『NAS』不止娱乐,NAS也是生产力,在绿联部署AI工作流工具-n8n》里提到的方法,在 Docker 的「镜像」模块里搜索 n8n 下载部署的话,需要这么做。

我用绿联 NAS 举例,其他品牌的 NAS 操作方法大同小异。

在 Docker 的「容器」里找到 n8n,停止运行。

然后编辑它。

在 NAS 的「文件管理」里创建一个文件夹,用来给 n8n 读写文件使用的。

然后在「编辑容器」的「存储空间」里添加一项 /home/node/.n8n-files 指向那个文件夹,提供“读写”权限,如下图红框所示。

点击“保存”按钮,然后运行项目。

我在 NAS 的 n8n-files 文件夹里准备了一个 雷猴世界.txt 文件。

在 n8n 里,使用 /home/node/.n8n-files/雷猴世界.txt 这个路径就能读取到上面这个文件了。

同样,也是这个格式:

/home/node/.n8n-files/文件名.后缀

以上就是本文的全部内容啦,想了解更多n8n玩法欢迎关注《n8n修炼手册》👏

如果你有 NAS,我非常建议你在 NAS 上部署一套 n8n,搞搞副业也好,帮你完成工作任务也好 《『NAS』不止娱乐,NAS也是生产力,在绿联部署AI工作流工具-n8n》

点赞 + 关注 + 收藏 = 学会了

在 Vite + React 项目中集成 Sentry 完整指南

📚 本教程将带你从零开始,一步步完成 Sentry 在 Vite + React 项目中的接入与配置,实现错误监控与源码映射功能。

📋 目录

3. 创建 Project

在组织中创建一个新项目:

配置项说明
Platform选择 React
Project name填写项目名称(如:vite-react-app
⚠️ 重要提示:创建完成后,请复制并保存好以下 DSN 地址,后续配置会用到:
https://4352b88f3321d7e98766b0b743fa7115@o4514356086109909.ingest.us.sentry.io/4510743223411211


三、初始化本地项目

使用 Vite 创建一个新的 React 项目:

npm create vite@latest my-react-app -- --template react

四、安装 Sentry 依赖

在项目根目录执行以下命令安装 Sentry 相关包:

npm install @sentry/react @sentry/vite-plugin
包名说明
@sentry/react浏览器错误捕获 + React Error Boundary
@sentry/vite-pluginSource Map 上传插件(关键组件)

五、配置 Sentry

1. 创建 Sentry 初始化文件

src 目录下新建 sentry.ts 文件:


import * as Sentry from "@sentry/react";
export function initSentry() {
  // 本地开发环境不自动上报,避免污染
  if (import.meta.env.DEV) {
    return;
  }
  Sentry.init({
    dsn: import.meta.env.VITE_SENTRY_DSN,
    integrations: [
      Sentry.browserTracingIntegration(),
    ],
    // 性能追踪采样率,生产环境建议 0.1 ~ 0.3
    tracesSampleRate: 1.0,
    
    // 设置环境标识
    environment: import.meta.env.MODE,
    // 在发送前可对事件进行脱敏处理
    beforeSend(event) {
      return event;
    },
  });
}

2. 在入口文件中初始化 Sentry

修改 main.tsx注意:必须在 React 渲染之前初始化 Sentry


import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { initSentry } from "./sentry";
// 初始化 Sentry(必须在渲染前调用)
initSentry();
ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

3. 配置环境变量

在项目根目录新建 .env.production 文件:


SENTRY_ORG=my-company-j3
SENTRY_PROJECT=vite-react-web
VITE_SENTRY_DSN=https://4352b88f3321d7e98766b0b743fa7115@o4514356086109909.ingest.us.sentry.io/4510743223411211
SENTRY_AUTH_TOKEN=<从Sentry控制台获取>
SENTRY_RELEASE=my-react-app@0.0.0
环境变量说明
变量名说明获取方式截图
SENTRY_ORG组织标识SettingsOrganizationGeneral Settings
SENTRY_PROJECT项目标识SettingsOrganizationProjects<项目名称>ProjectSlug
VITE_SENTRY_DSN数据源名称创建项目时显示的 DSN
SENTRY_AUTH_TOKEN认证令牌见下文「设置 Auth Token
SENTRY_RELEASE发布版本号根据实际项目填写(如:my-react-app@1.0.0

六、配置 Vite 插件

修改 vite.config.ts,配置 Sentry 插件以实现 Source Map 上传:


import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import { sentryVitePlugin } from '@sentry/vite-plugin'
export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '')
  return {
    build: {
      sourcemap: true,
    },
    define: {
      'import.meta.env.VITE_SENTRY_RELEASE': JSON.stringify(env.SENTRY_RELEASE),
    },
    plugins: [
      react(),
      sentryVitePlugin({
        org: env.SENTRY_ORG,
        project: env.SENTRY_PROJECT,
        authToken: env.SENTRY_AUTH_TOKEN,
        sourcemaps: {
          disable: 'disable-upload',
        },
        release: {
          name: env.SENTRY_RELEASE,
          uploadLegacySourcemaps: [
            {
              paths: ['dist/assets'],
              urlPrefix: '~/assets',
            },
          ],
          setCommits: false,
        },
      }),
    ],
  }
})
💡 提示:该配置解决了「在 Sentry 中看不到源码」的问题,通过上传 Source Map 可以精确定位到出错的具体代码行。

七、测试错误上报

修改 App.tsx,添加手动触发错误的按钮,方便测试:


import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import * as Sentry from '@sentry/react'
function App() {
  const [count, setCount] = useState(0)
  const onClickFn = () => {
    console.log('手动触发错误')
    try {
      throw new Error('手动触发错误')
    } catch (err) {
      Sentry.captureException(err)
    }
  }
  return (
    <>
      <div>
        <a href="https://vite.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <button onClick={() => Sentry.captureMessage('消息1')}>
          消息1
        </button>
        <button onClick={onClickFn}>
          手动触发错误
        </button>
        <p>
          Edit <code>src/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  )
}
export default App

八、设置 Auth Token

该步骤只需操作一次,用于获取 Source Map 上传权限。

1. 进入 Token 创建页面

路径:SettingsDeveloper SettingsPersonal Tokens

2. 创建新 Token

点击「Create New Token」,按以下配置权限:

权限类别选项
ProjectRead
ReleaseAdmin
OriganizationRead
其他选项保持默认 No Access

3. 复制 Token

创建成功后,立即复制生成的 Token(仅显示一次),将其填入 .env.production 文件中的 SENTRY_AUTH_TOKEN


九、构建与验证

1. 构建项目并上传 Source Maps

npm run build
# 或
yarn build

构建完成后,Sentry 插件会自动将 Source Maps 上传到 Sentry 服务器。

2. 预览项目

npm run preview
# 或
yarn preview

3. 触发错误

在浏览器中打开应用,点击「手动触发错误」按钮。如果一切配置正确,你应该能在 Sentry 中看到一条新的错误记录。


十、查看错误日志

在 Sentry 中查看上报的错误

登录 Sentry 官网,进入对应项目,即可看到捕获的错误日志,并且能够直接定位到源码,极大地方便了问题排查。

查看 Source Maps 上传情况

  1. 找到你的项目:SettingsOrganizationProjects<项目名称>

  1. 进入 Source Maps 页面:ProcessingSource Maps
    在这里你可以确认 Source Maps 是否成功上传。


🎉 总结

恭喜!你已经成功完成了 Sentry 在 Vite + React 项目中的完整配置。现在你的应用具备了:

功能说明
🔍 错误捕获自动捕获并上报运行时错误
📍 源码定位通过 Source Map 精确定位错误行号
📊 性能追踪可选的性能数据采样收集
🛡️ 环境隔离本地开发环境不污染生产数据

常见问题

<details>
<summary>❓ 为什么在 Sentry 中看不到源码?</summary>
请检查以下几点:

  1. vite.config.ts 中是否正确配置了 sentryVitePlugin
  2. .env.production 中的 SENTRY_AUTH_TOKEN 是否正确
  3. 构建时是否成功执行(查看控制台是否有上传日志)
    </details>
    <details>
    <summary>❓ 本地开发环境会上报错误吗?</summary>
    不会。在 sentry.ts 中我们添加了环境判断,只有非开发环境才会初始化 Sentry。

    if (import.meta.env.DEV) {
      return;
    }

    </details>

    希望这篇教程对你有所帮助!如有问题,欢迎交流讨论。

本文由mdnice多平台发布

cmd:node -v
re: 'node' 不是内部或外部命令,也不是可运行的程序或批处理文件。

切换到 ide 的终端有时候又有了,现在挺烦恼的,有可能 cmd 有 ide 没有 有时候 ide 有 cmd 没有 有时候两边都没有只能重启大法,重启大法也有时候也可能还会出现这情况,求助 v 友大神

缘起

只用服务器搭建 memos 未免太大材小用了,而且也浪费钱。所以就想尽量用无服务器部署 memos。

render

render 由于免费存储空间过低,不是优选。

  • 使用 render 创建 Web Service
  • Image:填写为 neosmemo/memos:stable
  • Environment Variables 分别填入:
    • Key、port
    • Value、5230

保活方式: https://github.com/hoochanlon/keep-alive

zeabur

memos 官方镜像按照如图所示填写相关参数

🖼️ 图片加载失败

使用 hu3rror/memos-litestream (该项目解决了备份换机迁移数据的痛点)项目镜像的填写方式

S3 配置如图及相关解答(建议看完该 issue 链接内容): https://github.com/hu3rror/memos-litestream/issues/67

  • b2

  • memos

CF 代理 B2 配置见(适用于图床、文件管理免流服务): https://github.com/hoochanlon/CF-Proxy-B2

我把 swarm 端口改成 4002 是因为 Planet 抢占了 4001 端口。


这脚本大概这么干活:

  1. 先瞅一眼:看看 Docker 装了没,别忙活半天白干。再检查一下有没有叫 ipfs_host 的老容器赖着不走,有的话就报错开溜,坚决不给自己留烂摊子。
  2. 搭俩小窝:在当前目录下建 ipfs_stagingipfs_data 两个文件夹,给 IPFS 的数据找个地方住。
  3. 门户大开(但换了号):因为 4001 被占了,咱就让 swarm 走 4002。API 端口是 8080,网关端口是 5001,按你的习惯随便改。
  4. 拉起来跑:用最新的 ipfs/kubo 镜像把容器跑起来,把刚才设的端口和文件夹都挂载好。
  5. 直接开门迎客:跑起来没问题的话,自动帮你打开浏览器,跳到 http://localhost:5001/webui 这个管理页面。接下来传点猫图试试手呗。


怎么用?

简单到不行:

复制
# 1. 给脚本加个执行权限
chmod +x deployment.sh

# 2. 运行它
./deployment.sh

脚本跑完没报错,你的浏览器就会蹦出 IPFS 的 Web 界面了。


脚本在这儿 (deployment.sh)

复制
#!/bin/bash

# 检查 Docker 是否安装
if ! [ -x "$(command -v docker)" ]; then
  echo 'Error: Docker is not installed.' >&2
  exit 1
fi
echo 'Docker is installed.'

# 检查是否有正在运行的 IPFS 容器
if [ "$(docker ps -q -f name=ipfs_host)" ]; then
  echo 'Error: An IPFS container is already running.' >&2
  exit 1
fi
echo 'No running IPFS container found.'

# 检查是否有同名的停止状态的 IPFS 容器
if [ "$(docker ps -aq -f status=exited -f name=ipfs_host)" ]; then
  echo 'Error: A stopped IPFS container with the same name already exists.' >&2
  exit 1
fi
echo 'No stopped IPFS container with the same name found.'

echo 'Proceeding with deployment...'
# 创建存储目录
current_dir=$(pwd)
cd $current_dir
mkdir -p ./ipfs_staging
mkdir -p ./ipfs_data

echo 'Storage directories created.'

# 设置环境变量
export ipfs_staging=./ipfs_staging
export ipfs_data=./ipfs_data
export ipfs_swarm_port=4002
export ipfs_api_port=8080
export ipfs_gateway_port=5001

echo "IPFS staging directory: ${ipfs_staging}"
echo "IPFS data directory: ${ipfs_data}"
echo "IPFS swarm port: ${ipfs_swarm_port}"
echo "IPFS API port: ${ipfs_api_port}"
echo "IPFS gateway port: ${ipfs_gateway_port}"

# 运行 IPFS 容器
docker run -d --name ipfs_host -v ${ipfs_staging}:/export -v ${ipfs_data}:/data/ipfs -p ${ipfs_swarm_port}:4001 -p ${ipfs_api_port}:8080 -p ${ipfs_gateway_port}:5001 ipfs/kubo:latest
if [ $? -ne 0 ]; then
  docker rm -f ipfs_host 2>/dev/null
  rm -rf ./ipfs_staging
  rm -rf ./ipfs_data
  echo 'Error: Failed to start the IPFS container.' >&2
  exit 1
fi
echo 'IPFS container started successfully.'

open "http://localhost:${ipfs_gateway_port}/webui"


说实话, AI 润色的确实不咋地 😂

背景

ESP32 的 Rust 教程示例项目中,第一个项目 hardware-check 演示了如何利用开发板自带的 WIFI 连接无线网。像 WIFI SSID 与密码 这样的变量当然不应该直接写进代码,示例中利用了 toml-cfg#toml_config 宏,读取 cfg.toml 中的值并注入代码(cfg.toml git ignore),以做到在运行时使用这些变量。

这里示例代码稍有误导:在 build.rs 中声明 struct 并调用了一次 #[toml_cfg::toml_config],随即对获取到的值做了一些简单检查。然后在 main.rs 中用完全一样的方法再次声明 struct 并调用宏,并在后续连接 WIFI 时使用。在 main.rs 中还加了一段注释:/// This configuration is picked up at compile time by build.rs from the file cfg.toml 。这似乎意味着在这两处相同的 struct 声明及宏调用处存在着某种魔法,使得编译期读取到的配置文件内容可以被传递到运行期使用。

但这里并不存在 build.rs 把值 “传给” main.rs 的魔法。toml_cfg::toml_config 是过程宏,它在编译期读取配置并生成 CONFIG 常量;示例里 build.rs 只是重复生成一份用于构建时校验,运行时真正使用的是应用 crate 中那份 CONFIG

我一不习惯 TOML,二不想为此多引一个包,于是想试试能否利用更简单直接的办法实现。

Cargo build-scripts

The Cargo book 3.8 Build Scripts 中介绍了构建脚本的一些常见用法与场景,其中就包含了 “使用 rustc-env 指令将指定的环境变量在编译时注入到编译后的 crate,并利用 env! 宏获取这些变量”。

那么类似的,我自然可以将所需的配置项,例如 WIFI 密码写入 .env (当然,git ignore)并在 build.rs 中读取解析,并 println!("cargo:rustc-env={}={}", key, value);

而在程序代码中,使用 let env_foo = env!("FOO").to_string() 将其展开。如此,配置文件中的值在编译期就会确定并注入到代码成为常量。

当然要注意,这里的 “环境变量” 始终仅发生在编译期。一旦编译结束,所有的值都已确定,被 env! 展开的值成为字符串字面量,连同代码一起被编译成二进制。

以上。


📌 转载信息
原作者:
Qi_Nark
转载时间:
2026/1/11 08:42:51

一个轻量的 CLI,用于在 Claude Code 与 Codex 的环境变量之间快速切换。功能比较简单,适合轻度使用。

快速开始

  1. 安装:
npm install -g @praeviso/code-env-switch
  1. 交互式添加 profile(若不存在会创建 ~/.config/code-env/config.json):
codenv add
# 再执行一次,用来添加另一种 type
codenv add

交互示例:

$ codenv add
Select type (1=codex, 2=claude): 1
Profile name (default: default): primary
Base URL (required): https://api.example.com/v1
API key (required): YOUR_API_KEY
  1. 按 type 设置默认项:
codenv default codex primary
codenv default claude default
  1. 启用自动应用:
codenv init
新开终端(或执行 `source ~/.bashrc` / `source ~/.zshrc`)即可自动应用默认配置。
  1. 交互式选择:
codenv use 或者 codenv use claude mirror

不会永久污染环境变量,原理是每次 bash 启动的时候自动执行脚本 export 对应的环境变量。

测试下来,切换 claude 的时候会显示上一个 proflie 的 api key,但是实际上是已经切换过来了,可以用 status 查看。

此外不知道是否会和原有的 cc/codex 的 config 文件里面的 apikey 冲突,我还没有测试。


📌 转载信息
转载时间:
2026/1/6 11:57:35

看佬友安装工具来切换配置,可能有人像我一样,不喜欢安装,这里有一种简易通过环境变量自由修改 cc 配置的方法:

1、终端执行:nano ~/.zshrc
2、写入配置并保存,以 glm 为例:

# Claude code (glm) glm() {
    export ANTHROPIC_BASE_URL=https://open.bigmodel.cn/api/anthropic
    export ANTHROPIC_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxx
    claude
}

3、终端执行:source ~/.zshrc
4、使用的时候只需要输入 glm 即可启动。可以在第 2 步里自定义更多的参数。


📌 转载信息
原作者:
ggvisPro
转载时间:
2026/1/2 16:13:59