惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
人人都是产品经理
人人都是产品经理
IT之家
IT之家
Cyberwarzone
Cyberwarzone
T
Troy Hunt's Blog
有赞技术团队
有赞技术团队
阮一峰的网络日志
阮一峰的网络日志
T
Threat Research - Cisco Blogs
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 叶小钗
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
The Hacker News
The Hacker News
Jina AI
Jina AI
T
Tor Project blog
V
Vulnerabilities – Threatpost
酷 壳 – CoolShell
酷 壳 – CoolShell
Spread Privacy
Spread Privacy
博客园_首页
C
Cybersecurity and Infrastructure Security Agency CISA
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Simon Willison's Weblog
Simon Willison's Weblog
Security Latest
Security Latest
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 司徒正美
V2EX - 技术
V2EX - 技术
I
Intezer
The Cloudflare Blog
Cisco Talos Blog
Cisco Talos Blog
SecWiki News
SecWiki News
博客园 - 【当耐特】
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
Lohrmann on Cybersecurity
Scott Helme
Scott Helme
Google Online Security Blog
Google Online Security Blog
量子位
The Last Watchdog
The Last Watchdog
AI
AI
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Security Affairs
P
Palo Alto Networks Blog
S
Secure Thoughts
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Attack and Defense Labs
Attack and Defense Labs

博客园 - 无心々菜

高并发 - 配置2 高并发 - 配置 地图 - QGIS加载geojson,并导入Postgis数据库 VUE2 - Geoserver打印矢量PDF地图 - 在VUE2中的应用 地图 - Geoserver打印矢量PDF地图 Docker - 离线+无root环境安装 docker-rootless 运维 - 哪吒监控 Postgres 数据库在docker环境下分布式部署(arm64框架) Postgres 数据库在docker环境下分布式部署 Https - 配置方案 VUE3 - Docker部署 + traefik反代 VUE2 - Docker部署 + traefik反代 高斯DB 数据库的安装 NETCORE - 使用 EFCORE 调用 高斯DB数据库 JMeter - 压力测试 NETCORE - IdentityServer4 多节点部署 NETCORE - IdentityServer4 相关文档 .NET框架 - NETCORE + API + EF(DBFirst) + PostgresSql Docker 部署 APISIX + etcd Docker部署martin 矢量切片服务器 Postgres 数据库的安装 NETCORE - 调用 RUSTFS Docker - 部署 RustFS 对像存储 Docker - 使用 Docker Secrets 敏感数据传输 Docker - 部署 Consul KV 统一管理所有项目的配置 Docker - 部署 HashiCorp Vault 开源密钥管理工具 kong 网关下集成 Consul服务注册与发现 Docker 部署 kong 网关 Docker - 部署Consul 新
OpenObserver - 系统接口监控
无心々菜 · 2026-04-17 · via 博客园 - 无心々菜

 OpenObserver - 系统接口监控

1. 拉取镜像

docker pull public.ecr.aws/zinclabs/openobserve:latest

2. docker-compose

  openobserve:
    image: public.ecr.aws/zinclabs/openobserve:latest
    container_name: openobserve
    restart: unless-stopped
    ports:
      - "5080:5080"
    volumes:
      - ./openobserve-data:/data
    environment:
      ZO_ROOT_USER_EMAIL: "root@example.com"
      ZO_ROOT_USER_PASSWORD: "Pass*No01"
      

3. 启动

docker-compose up  -d openobserve

4. 打开浏览器

http://localhost:5080/

image

image

5.   .net8 项目中安装 nuget 包

dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http

image

6. 项目中 Program.cs


#region OpenTelemetry
builder.Services.AddMyOpenTelemetry(configuration);
#endregion

7. 增加文件 OpenTelemetryServiceExtensions.cs

查看代码
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using OpenTelemetry.Instrumentation.AspNetCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;

namespace Rail.Medium.Middleware
{
    public static class OpenTelemetryServiceExtensions
    {
        public static IServiceCollection AddMyOpenTelemetry(
            this IServiceCollection services,
            IConfiguration configuration)
        {
            var otelConfig = configuration.GetSection("OpenTelemetry");

            if (otelConfig == null)
            {
                Console.WriteLine("[警告] OpenTelemetry 配置不存在");
                return services;
            }

            var endpoint = otelConfig["Endpoint"];
            var username = otelConfig["Credentials:Username"];
            var password = otelConfig["Credentials:Password"];
            var serviceName = otelConfig["ServiceName"];

            if (string.IsNullOrWhiteSpace(endpoint) ||
                string.IsNullOrWhiteSpace(username) ||
                string.IsNullOrWhiteSpace(password) ||
                string.IsNullOrWhiteSpace(serviceName))
            {
                Console.WriteLine("[警告] OpenTelemetry 配置不完整");
                return services;
            }

            var credentials = Convert.ToBase64String(
                Encoding.UTF8.GetBytes($"{username}:{password}"));

            var resourceBuilder = ResourceBuilder.CreateDefault()
                .AddService(serviceName)
                .AddAttributes(new Dictionary<string, object>
                {
                    ["environment"] = otelConfig["Environment"] ?? "unknown",
                    ["service.version"] = otelConfig["ServiceVersion"] ?? "unknown"
                });

            services.AddOpenTelemetry()
                .WithTracing(tracing => tracing
                    .SetResourceBuilder(resourceBuilder)
                    .AddAspNetCoreInstrumentation(options => ConfigureAspNetCore(options))
                    .AddHttpClientInstrumentation()
                    .AddOtlpExporter(opt =>
                    {
                        opt.Endpoint = new Uri(endpoint);
                        opt.Headers = $"Authorization=Basic {credentials}";
                        opt.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.HttpProtobuf;
                    }));

            return services;
        }

        private static void ConfigureAspNetCore(AspNetCoreTraceInstrumentationOptions options)
        {
            options.EnrichWithHttpRequest = (activity, request) =>
             {
                 SetClientIp(request, activity);
                 SetUserAgent(request, activity);
                 SetServerInfo(activity);
                 SetLoginRequestInfo(request, activity);
                 TryReadUserFromJwt(request, activity);
             };

            options.EnrichWithHttpResponse = (activity, response) =>
            {
                SetLoginResult(response, activity);
            };


        }

        //=========================================================
        // 客户端 IP
        //=========================================================
        private static void SetClientIp(HttpRequest request, Activity activity)
        {
            string ip = null;

            var xff = request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (!string.IsNullOrWhiteSpace(xff))
            {
                ip = xff.Split(',')[0].Trim();
            }

            if (string.IsNullOrWhiteSpace(ip))
            {
                ip = request.Headers["X-Real-IP"].FirstOrDefault();
            }

            if (string.IsNullOrWhiteSpace(ip))
            {
                ip = request.HttpContext.Connection.RemoteIpAddress?.ToString();
            }

            if (!string.IsNullOrWhiteSpace(ip))
            {
                activity?.SetTag("client.ip", ip);
            }
        }

        //=========================================================
        // 浏览器信息
        //=========================================================
        private static void SetUserAgent(HttpRequest request, Activity activity)
        {
            var ua = request.Headers["User-Agent"].FirstOrDefault();
            if (!string.IsNullOrWhiteSpace(ua))
            {
                activity?.SetTag("user.agent", ua);
            }
        }

        //=========================================================
        // 当前机器 / Docker
        //=========================================================
        private static void SetServerInfo(Activity activity)
        {
            activity?.SetTag("server.name", Environment.MachineName);
            activity?.SetTag("container.name", Environment.GetEnvironmentVariable("HOSTNAME"));
        }

        //=========================================================
        // 登录请求识别
        //=========================================================
        private static void SetLoginRequestInfo(HttpRequest request, Activity activity)
        {
            var path = request.Path.Value?.ToLower();

            if (path != "/connect/token")
                return;

            activity?.SetTag("auth.endpoint", "login");

            if (!request.HasFormContentType)
                return;

            var grantType = request.Form["grant_type"].FirstOrDefault();
            if (!string.IsNullOrWhiteSpace(grantType))
            {
                activity?.SetTag("grant.type", grantType);
            }

            var clientId = request.Form["client_id"].FirstOrDefault();
            if (!string.IsNullOrWhiteSpace(clientId))
            {
                activity?.SetTag("client.id", clientId);
            }

            var username = request.Form["username"].FirstOrDefault();
            if (!string.IsNullOrWhiteSpace(username))
            {
                activity?.SetTag("login.username", username);
            }
        }

        //=========================================================
        // 登录结果
        //=========================================================
        private static void SetLoginResult(HttpResponse response, Activity activity)
        {
            var path = response.HttpContext.Request.Path.Value?.ToLower();

            if (path != "/connect/token")
                return;

            if (response.StatusCode == 200)
            {
                activity?.SetTag("login.result", "success");
            }
            else
            {
                activity?.SetTag("login.result", "fail");
            }
        }

        //=========================================================
        // 解析 JWT Token
        //=========================================================
        private static void TryReadUserFromJwt(HttpRequest request, Activity activity)
        {
            var auth = request.Headers["Authorization"].FirstOrDefault();

            if (string.IsNullOrWhiteSpace(auth))
                return;

            if (!auth.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            var token = auth.Substring(7).Trim();

            if (string.IsNullOrWhiteSpace(token))
                return;

            try
            {
                var handler = new JwtSecurityTokenHandler();

                if (!handler.CanReadToken(token))
                    return;

                var jwt = handler.ReadJwtToken(token);

                // 用户ID
                var userId = GetClaim(jwt, "sub", ClaimTypes.NameIdentifier);
                if (!string.IsNullOrWhiteSpace(userId))
                {
                    activity?.SetTag("user.id", userId);
                }

                // 用户名
                var userName = GetClaim(jwt, "name", ClaimTypes.Name, "preferred_username");
                if (!string.IsNullOrWhiteSpace(userName))
                {
                    activity?.SetTag("user.name", userName);
                }

                // client_id
                var clientId = GetClaim(jwt, "client_id");
                if (!string.IsNullOrWhiteSpace(clientId))
                {
                    activity?.SetTag("client.id", clientId);
                }

                // 角色
                var roles = jwt.Claims
                    .Where(x => x.Type == "role" || x.Type == ClaimTypes.Role)
                    .Select(x => x.Value)
                    .Distinct()
                    .ToArray();

                if (roles.Length > 0)
                {
                    activity?.SetTag("user.roles", string.Join(",", roles));
                }

                // scope
                var scope = GetClaim(jwt, "scope");
                if (!string.IsNullOrWhiteSpace(scope))
                {
                    activity?.SetTag("scope", scope);
                }

                // tenant
                var tenant = GetClaim(jwt, "tenant_id", "tenant");
                if (!string.IsNullOrWhiteSpace(tenant))
                {
                    activity?.SetTag("tenant.id", tenant);
                }
            }
            catch
            {
                // token 异常忽略
            }
        }

        //=========================================================
        // Claim读取助手
        //=========================================================
        private static string GetClaim(JwtSecurityToken jwt, params string[] names)
        {
            foreach (var name in names)
            {
                var value = jwt.Claims.FirstOrDefault(x => x.Type == name)?.Value;
                if (!string.IsNullOrWhiteSpace(value))
                {
                    return value;
                }
            }

            return null;
        }
    }
}

8. 测试 

调用接口后,查看 http://localhost:5080/web/

可在以下菜单查看

  数据流-追踪

  追踪

  日志

image

end,