使用 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

捕获第二个请求的正文