返回正文
Are you an LLM? You can read better optimized documentation at /zh-CN/blog/2025-10/Ocelot-Serilog-Loki.md for this page in Markdown format
将 Ocelot 与 Serilog 和 Loki 集成 
将 Ocelot 与 Serilog 和 Loki 集成可以实现强大的日志收集和分析能力。Loki 是一个轻量级的日志聚合系统,非常适合与 Serilog 结合使用。以下是具体实现步骤:
安装必要的包 
首先安装 Serilog 及其 Loki 适配器:
bash
Install-Package Serilog.AspNetCore
Install-Package Serilog.Sinks.Loki
Install-Package Serilog.Enrichers.Environment
Install-Package Serilog.Enrichers.Thread1
2
3
4
2
3
4
配置 Serilog 与 Loki 集成 
在 Program.cs 中配置 Serilog 以将日志发送到 Loki:
csharp
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Serilog;
using Serilog.Sinks.Loki;
using System.Collections.Generic;
using System.Net.Http;
// 配置 Serilog 与 Loki 集成
Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .Enrich.WithEnvironmentName()
    .Enrich.WithMachineName()
    .Enrich.WithThreadId()
    .WriteTo.Console() // 同时输出到控制台
    .WriteTo.LokiHttp(
        "http://localhost:3100", // Loki 服务器地址
        new List<LokiLabel> // 添加标签用于日志过滤
        {
            new LokiLabel("app", "ocelot-gateway"),
            new LokiLabel("environment", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "production")
        },
        httpClient: new HttpClient(new HttpClientHandler
        {
            ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true // 忽略 HTTPS 证书验证(仅开发环境)
        }))
    .CreateLogger();
try
{
    Log.Information("Starting Ocelot gateway");
    
    var builder = WebApplication.CreateBuilder(args);
    
    // 使用 Serilog
    builder.Host.UseSerilog();
    
    // 配置 Ocelot
    builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
    builder.Services.AddOcelot(builder.Configuration);
    
    // 其他服务配置(认证、授权等)
    // ...
    
    var app = builder.Build();
    
    // 启用自定义日志中间件(如果需要)
    app.UseMiddleware<RequestLoggingMiddleware>();
    
    // 启用认证和授权
    app.UseAuthentication();
    app.UseAuthorization();
    
    // 启用 Ocelot
    await app.UseOcelot();
    
    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Ocelot gateway terminated unexpectedly");
}
finally
{
    Log.CloseAndFlush();
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
Program.cs
配置 Loki 连接参数 
建议将 Loki 连接参数配置在 appsettings.json 中,方便管理:
json
    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning",
          "Ocelot": "Information"
        }
      },
      "Loki": {
        "Url": "http://localhost:3100",
        "ApplicationName": "ocelot-gateway",
        "TimeoutSeconds": 5
      }
    }1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
appsettings.json
然后修改 Program.cs 从配置文件读取参数:
csharp
// 从配置文件读取 Loki 配置
var lokiUrl = builder.Configuration["Loki:Url"];
var appName = builder.Configuration["Loki:ApplicationName"];
Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .Enrich.WithEnvironmentName()
    .Enrich.WithMachineName()
    .WriteTo.Console()
    .WriteTo.LokiHttp(
        lokiUrl,
        new List<LokiLabel>
        {
            new LokiLabel("app", appName),
            new LokiLabel("environment", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "production")
        })
    .CreateLogger();1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Program.cs
扩展自定义日志中间件以适应 Loki 
修改之前的请求日志中间件,使其生成更适合 Loki 的结构化日志:
csharp
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace OcelotGateway.Middleware
{
    public class LokiRequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<LokiRequestLoggingMiddleware> _logger;
        public LokiRequestLoggingMiddleware(RequestDelegate next, ILogger<LokiRequestLoggingMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            var stopwatch = Stopwatch.StartNew();
            var requestTime = DateTime.UtcNow;
            
            // 保存原始响应流
            var originalResponseBodyStream = context.Response.Body;
            
            // 替换响应流以便捕获响应内容
            using (var responseBodyStream = new MemoryStream())
            {
                context.Response.Body = responseBodyStream;
                try
                {
                    // 处理请求
                    await _next(context);
                    stopwatch.Stop();
                    // 收集请求信息
                    var logData = new
                    {
                        Method = context.Request.Method,
                        Path = context.Request.Path,
                        Query = context.Request.QueryString.ToString(),
                        ClientIp = context.Connection.RemoteIpAddress?.ToString(),
                        StatusCode = context.Response.StatusCode,
                        ElapsedMs = stopwatch.ElapsedMilliseconds,
                        Timestamp = requestTime
                    };
                    // 记录结构化日志
                    _logger.LogInformation(
                        "Request processed: {Method} {Path} {StatusCode} {ElapsedMs}ms",
                        logData.Method, logData.Path, logData.StatusCode, logData.ElapsedMs,
                        logData);
                }
                catch (Exception ex)
                {
                    stopwatch.Stop();
                    _logger.LogError(ex, "Error processing request: {Method} {Path}", 
                        context.Request.Method, context.Request.Path);
                    throw;
                }
                finally
                {
                    // 将响应内容复制回原始流
                    await responseBodyStream.CopyToAsync(originalResponseBodyStream);
                    context.Response.Body = originalResponseBodyStream;
                }
            }
        }
    }
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
启动 Loki 和 Grafana 
为了接收和可视化日志,需要运行 Loki 和 Grafana:
使用 Docker 快速启动:
yml
# 创建 docker-compose.yml 文件
version: "3"
services:
  loki:
    image: grafana/loki:latest
    ports:
      - "3100:3100"
    volumes:
      - ./loki-config.yml:/etc/loki/local-config.yaml
    command: -config.file=/etc/loki/local-config.yaml
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    depends_on:
      - loki
volumes:
  grafana-data:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
启动服务:
bash
docker-compose up -d1
在 Grafana 中添加 Loki 数据源:
- 访问 http://localhost:3000(默认用户名/密码:admin/admin)
- 导航到 Configuration > Data Sources
- 点击 "Add data source" 并选择 Loki
- 设置 URL 为 http://loki:3100
- 点击 "Save & Test"
验证日志流程 
- 启动 Ocelot 网关
- 发送一些请求到网关
- 在 Grafana 中创建仪表盘或使用 Explore 功能查询日志
- 使用标签查询示例:{app="ocelot-gateway"}
最佳实践 
- 合理设置日志级别:在生产环境中避免过多的 Debug 日志
- 添加有意义的标签:如服务名、环境、版本等,便于日志过滤
- 敏感信息过滤:确保日志中不包含密码、令牌等敏感数据
- 设置超时和重试:配置 Loki 客户端的超时和重试机制
- 监控 Loki 健康状态:确保日志系统本身的可用性
通过这种集成方式,你可以利用 Loki 的高效日志存储和 Grafana 的强大可视化能力,构建一个完整的 Ocelot 网关日志监控系统。
VN/A | 
本站访客数
--次 本站总访问量
--人次 