使用 OnRequestCompleted 进行日志记录

编辑

使用 OnRequestCompleted 进行日志记录

编辑

在构建传递给客户端的连接设置时,您可以将类型为 Action<IApiCallDetails> 的回调传递到 OnRequestCompleted 方法,该方法可以在每次收到响应(好或坏)时进行监听。

如果您有复杂的日志记录需求,这是一个很好的添加位置,因为您可以访问请求和响应详细信息。

在此示例中,我们将使用连接设置上的 OnRequestCompleted 在每次调用时递增计数器。

var counter = 0;
var client = new ElasticClient(new AlwaysInMemoryConnectionSettings().OnRequestCompleted(r => counter++)); 

client.RootNodeInfo(); 
counter.Should().Be(1);

await client.RootNodeInfoAsync(); 
counter.Should().Be(2);

构建客户端

进行同步调用并断言计数器已递增

进行异步调用并断言计数器已递增

OnRequestCompleted 即使抛出异常也会被调用,因此即使客户端配置为抛出异常,它也可以使用。

var counter = 0;
var client = FixedResponseClient.Create( 
    new { },
    500,
    connectionSettings => connectionSettings
        .ThrowExceptions() 
        .OnRequestCompleted(r => counter++)
);

Assert.Throws<TransportException>(() => client.RootNodeInfo()); 
counter.Should().Be(1);

await Assert.ThrowsAsync<TransportException>(async () => await client.RootNodeInfoAsync());
counter.Should().Be(2);

配置一个始终返回 HTTP 500 响应的连接的客户端

调用导致异常时始终抛出异常

断言抛出异常且计数器已递增

以下是如何使用 OnRequestCompleted() 进行更复杂日志记录的示例

默认情况下,客户端直接写入请求流并直接从响应流反序列化。

如果您还想捕获请求和/或响应字节,还需要将 .DisableDirectStreaming() 设置为 true

var list = new List<string>();
var connectionPool = new SingleNodeConnectionPool(new Uri("https://127.0.0.1:9200"));

var settings = new ConnectionSettings(connectionPool, new InMemoryConnection()) 
    .DefaultIndex("default-index")
    .DisableDirectStreaming() 
    .OnRequestCompleted(apiCallDetails => 
    {
        // log out the request and the request body, if one exists for the type of request
        if (apiCallDetails.RequestBodyInBytes != null)
        {
            list.Add(
                $"{apiCallDetails.HttpMethod} {apiCallDetails.Uri} " +
                $"{Encoding.UTF8.GetString(apiCallDetails.RequestBodyInBytes)}");
        }
        else
        {
            list.Add($"{apiCallDetails.HttpMethod} {apiCallDetails.Uri}");
        }

        // log out the response and the response body, if one exists for the type of response
        if (apiCallDetails.ResponseBodyInBytes != null)
        {
            list.Add($"Status: {apiCallDetails.HttpStatusCode}" +
                     $"{Encoding.UTF8.GetString(apiCallDetails.ResponseBodyInBytes)}");
        }
        else
        {
            list.Add($"Status: {apiCallDetails.HttpStatusCode}");
        }
    });

var client = new ElasticClient(settings);

var syncResponse = client.Search<object>(s => s 
    .AllIndices()
    .Scroll("2m")
    .Sort(ss => ss
        .Ascending(SortSpecialField.DocumentIndexOrder)
    )
);

list.Count.Should().Be(2);

var asyncResponse = await client.SearchAsync<object>(s => s 
    .AllIndices()
    .Scroll("10m")
    .Sort(ss => ss
        .Ascending(SortSpecialField.DocumentIndexOrder)
    )
);

list.Count.Should().Be(4);
list.Should().BeEquivalentTo(new[] 
{
    @"POST https://127.0.0.1:9200/_all/_search?typed_keys=true&scroll=2m {""sort"":[{""_doc"":{""order"":""asc""}}]}",
    @"Status: 200",
    @"POST https://127.0.0.1:9200/_all/_search?typed_keys=true&scroll=10m {""sort"":[{""_doc"":{""order"":""asc""}}]}",
    @"Status: 200"
});

这里我们使用 InMemoryConnection,但在实际应用程序中,您将使用一个实际发送请求的 IConnection,例如 HttpConnection

禁用直接流式传输,以便我们可以捕获请求和响应字节

请求完成后执行某些操作。在这里,我们只是添加到列表中,但在您的应用程序中,您可能会将日志记录到文件中。

进行同步调用

进行异步调用

断言列表包含传递给 OnRequestCompleted 的委托中写入的内容

在生产环境中运行应用程序时,您可能不希望为所有请求禁用直接流式传输,因为这样做会由于在内存中缓冲请求和响应字节而导致性能开销。但是,它可以用于以临时方式捕获请求和响应,例如在生产环境中排除故障。

DisableDirectStreaming 可以为此目的在每个请求的基础上启用。使用此功能时,可以在 OnRequestCompleted 中配置一个通用的日志记录机制,并且仅在必要时记录请求和响应。

var list = new List<string>();
var connectionPool = new SingleNodeConnectionPool(new Uri("https://127.0.0.1:9200"));

var settings = new ConnectionSettings(connectionPool, new InMemoryConnection())
    .DefaultIndex("default-index")
    .OnRequestCompleted(apiCallDetails =>
    {
        // log out the request and the request body, if one exists for the type of request
        if (apiCallDetails.RequestBodyInBytes != null)
        {
            list.Add(
                $"{apiCallDetails.HttpMethod} {apiCallDetails.Uri} " +
                $"{Encoding.UTF8.GetString(apiCallDetails.RequestBodyInBytes)}");
        }
        else
        {
            list.Add($"{apiCallDetails.HttpMethod} {apiCallDetails.Uri}");
        }

        // log out the response and the response body, if one exists for the type of response
        if (apiCallDetails.ResponseBodyInBytes != null)
        {
            list.Add($"Status: {apiCallDetails.HttpStatusCode}" +
                     $"{Encoding.UTF8.GetString(apiCallDetails.ResponseBodyInBytes)}");
        }
        else
        {
            list.Add($"Status: {apiCallDetails.HttpStatusCode}");
        }
    });

var client = new ElasticClient(settings);

var syncResponse = client.Search<object>(s => s 
    .AllIndices()
    .Scroll("2m")
    .Sort(ss => ss
        .Ascending(SortSpecialField.DocumentIndexOrder)
    )
);

list.Count.Should().Be(2);

var asyncResponse = await client.SearchAsync<object>(s => s 
    .RequestConfiguration(r => r
        .DisableDirectStreaming()
    )
    .AllIndices()
    .Scroll("10m")
    .Sort(ss => ss
        .Ascending(SortSpecialField.DocumentIndexOrder)
    )
);

list.Count.Should().Be(4);
list.Should().BeEquivalentTo(new[]
{
    @"POST https://127.0.0.1:9200/_all/_search?typed_keys=true&scroll=2m", 
    @"Status: 200",
    @"POST https://127.0.0.1:9200/_all/_search?typed_keys=true&scroll=10m {""sort"":[{""_doc"":{""order"":""asc""}}]}", 
    @"Status: 200"
});

进行同步调用,其中不会缓冲请求和响应字节

进行异步调用,其中启用了 DisableDirectStreaming()

仅捕获第一个请求的方法和 url

捕获第二个请求的主体