使用 Azure API 管理、事件中心和 Moesif 监视 APIMonitor your APIs with Azure API Management, Event Hubs, and Moesif

API 管理服务提供许多功能来增强发送到 HTTP API 的 HTTP 请求的处理。The API Management service provides many capabilities to enhance the processing of HTTP requests sent to your HTTP API. 但是,请求和响应都是暂时性存在的。However, the existence of the requests and responses is transient. 在请求发出后,将通过 API 管理服务流送到后端 API。The request is made and it flows through the API Management service to your backend API. API 将处理该请求,然后将响应返回给 API 使用者。Your API processes the request and a response flows back through to the API consumer. API 管理服务保留要在 Azure 门户仪表板中显示的有关 API 的一些重要统计信息,但除此之外不显示详细信息。The API Management service keeps some important statistics about the APIs for display in the Azure portal dashboard, but beyond that, the details are gone.

在 API 管理服务中使用 log-to-eventhub 策略,可将请求和响应的详细信息发送到 Azure 事件中心By using the log-to-eventhub policy in the API Management service, you can send any details from the request and response to an Azure Event Hub. 想要从发送到 API 的 HTTP 消息生成事件的原因包罗万象。There are a variety of reasons why you may want to generate events from HTTP messages being sent to your APIs. 示例包括更新审核线索、使用情况分析、异常警报和第三方集成。Some examples include audit trail of updates, usage analytics, exception alerting, and third-party integrations.

本文演示如何捕获整个 HTTP 请求和响应消息、将其发送到事件中心,然后将该消息中继到可提供 HTTP 日志记录和监视服务的第三方服务。This article demonstrates how to capture the entire HTTP request and response message, send it to an Event Hub and then relay that message to a third-party service that provides HTTP logging and monitoring services.

为何要从 API 管理服务发送?Why Send From API Management Service?

可以编写能够插入到 HTTP API 框架中的 HTTP 中间件,以捕获 HTTP 请求和响应并将其馈送到日志记录和监视系统。It is possible to write HTTP middleware that can plug into HTTP API frameworks to capture HTTP requests and responses and feed them into logging and monitoring systems. 此方法的缺点是 HTTP 中间件必须集成到后端 API,并且必须与 API 的平台匹配。The downside to this approach is the HTTP middleware needs to be integrated into the backend API and must match the platform of the API. 如果有多个 API,每个 API 都必须部署中间件。If there are multiple APIs, then each one must deploy the middleware. 后端 API 无法升级往往是有原因的。Often there are reasons why backend APIs cannot be updated.

使用 Azure API 管理服务来与日志记录基础结构集成提供了一个集中的、平台独立的解决方案。Using the Azure API Management service to integrate with logging infrastructure provides a centralized and platform-independent solution. 此外,还可以借助 Azure API 管理的异地复制功能进行缩放。It is also scalable, in part due to the geo-replication capabilities of Azure API Management.

为何要发送到 Azure 事件中心?Why send to an Azure Event Hub?

一个合理的问题是,为何要创建特定于 Azure 事件中心的策略?It is a reasonable to ask, why create a policy that is specific to Azure Event Hubs? 在许多不同的场合下,我可能想要记录我的请求。There are many different places where I might want to log my requests. 为何不能直接将请求发送到最终目标?Why not just send the requests directly to the final destination? 这只是一个选项。That is an option. 但是,从 API 管理服务发出日志记录请求时,必须考虑日志记录消息对 API 性能造成的影响。However, when making logging requests from an API management service, it is necessary to consider how logging messages impact the performance of the API. 增加系统组件的可用实例或使用异地复制功能,可以处理逐渐增加的负载。Gradual increases in load can be handled by increasing available instances of system components or by taking advantage of geo-replication. 但是,如果日志记录基础结构在低负载状态下开始变慢,则短期的流量高峰可能导致请求延迟。However, short spikes in traffic can cause requests to be delayed if requests to logging infrastructure start to slow under load.

Azure 事件中心旨在引入大量数据,它能够处理的事件数目远高于大多数 API 所处理的 HTTP 请求数目。The Azure Event Hubs is designed to ingress huge volumes of data, with capacity for dealing with a far higher number of events than the number of HTTP requests most APIs process. 在某种意义上,事件中心充当 API 管理服务与基础结构之间的高级缓冲区,可存储和处理消息。The Event Hub acts as a kind of sophisticated buffer between your API management service and the infrastructure that stores and processes the messages. 这可以确保 API 性能不受日志记录基础结构的影响。This ensures that your API performance will not suffer due to the logging infrastructure.

数据传递到事件中心后,会持久保存并等待事件中心使用者的处理。Once the data has been passed to an Event Hub, it is persisted and will wait for Event Hub consumers to process it. 事件中心不在乎其处理方式,只在乎如何确保成功传递消息。The Event Hub does not care how it is processed, it just cares about making sure the message will be successfully delivered.

事件中心可将事件流式传输到多个使用者组。Event Hubs has the ability to stream events to multiple consumer groups. 这样,事件便可由不同的系统处理。This allows events to be processed by different systems. 此外,可为许多集成方案提供支持,而不会在 API 管理服务中处理 API 请求时造成额外的延迟,因为只需生成一个事件。This enables supporting many integration scenarios without putting addition delays on the processing of the API request within the API Management service as only one event needs to be generated.

用于发送 application/http 消息的策略A policy to send application/http messages

事件中心接受简单字符串形式的事件数据。An Event Hub accepts event data as a simple string. 该字符串的内容由你确定。The contents of that string are up to you. 要打包 HTTP 请求并将其发送到事件中心,需要使用请求或响应信息来格式化字符串。To be able to package up an HTTP request and send it off to Event Hubs, we need to format the string with the request or response information. 在此类情况下,如果有可以重复使用的现有格式,则不需要编写自己的分析代码。In situations like this, if there is an existing format we can reuse, then we may not have to write our own parsing code. 最初,我考虑使用 HAR 来发送 HTTP 请求和响应。Initially I considered using the HAR for sending HTTP requests and responses. 但是,这种格式最适合用于存储 JSON 格式的一连串 HTTP 请求。However, this format is optimized for storing a sequence of HTTP requests in a JSON-based format. 其中包含一些必需的元素,使得通过网络传递 HTTP 消息的方案增加了不必要的复杂性。It contained a number of mandatory elements that added unnecessary complexity for the scenario of passing the HTTP message over the wire.

另一个做法是使用 HTTP 规范 RFC 7230 中所述的 application/http 媒体类型。An alternative option was to use the application/http media type as described in the HTTP specification RFC 7230. 此媒体类型使用的格式与用于通过网络实际发送 HTTP 消息的格式完全相同,但整个消息可以放在另一个 HTTP 请求的正文中。This media type uses the exact same format that is used to actually send HTTP messages over the wire, but the entire message can be put in the body of another HTTP request. 在本例中,我们将使用该正文作为消息发送到事件中心。In our case, we are just going to use the body as our message to send to Event Hubs. Microsoft ASP .NET Web API 2.2 客户端库中有一个分析器可以分析此格式并将其转换为本机 HttpRequestMessageHttpResponseMessage 对象,相当方便。Conveniently, there is a parser that exists in Microsoft ASP.NET Web API 2.2 Client libraries that can parse this format and convert it into the native HttpRequestMessage and HttpResponseMessage objects.

若要创建此消息,需要在 Azure API 管理中使用基于 C# 的策略表达式To be able to create this message, we need to take advantage of C# based Policy expressions in Azure API Management. 下面是可将 HTTP 请求消息发送到 Azure 事件中心的策略。Here is the policy, which sends an HTTP request message to Azure Event Hubs.

<log-to-eventhub logger-id="conferencelogger" partition-id="0">
@{
   var requestLine = string.Format("{0} {1} HTTP/1.1\r\n",
                                               context.Request.Method,
                                               context.Request.Url.Path + context.Request.Url.QueryString);

   var body = context.Request.Body?.As<string>(true);
   if (body != null && body.Length > 1024)
   {
       body = body.Substring(0, 1024);
   }

   var headers = context.Request.Headers
                          .Where(h => h.Key != "Authorization" && h.Key != "Ocp-Apim-Subscription-Key")
                          .Select(h => string.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))
                          .ToArray<string>();

   var headerString = (headers.Any()) ? string.Join("\r\n", headers) + "\r\n" : string.Empty;

   return "request:"   + context.Variables["message-id"] + "\n"
                       + requestLine + headerString + "\r\n" + body;
}
</log-to-eventhub>

策略声明Policy declaration

此策略表达式有一些值得一提的特别之处。There a few particular things worth mentioning about this policy expression. log-to-eventhub 策略有一个名为 logger-id 的属性,该属性引用在 API 管理服务中创建的记录器名称。The log-to-eventhub policy has an attribute called logger-id, which refers to the name of logger that has been created within the API Management service. 如何在 Azure API 管理中将事件记录到 Azure 事件中心文档中提供了有关如何在 API 管理服务中设置事件中心记录器的详细信息。The details of how to set up an Event Hub logger in the API Management service can be found in the document How to log events to Azure Event Hubs in Azure API Management. 第二个属性是一个可选参数,指明事件中心要在哪个分区中存储消息。The second attribute is an optional parameter that instructs Event Hubs which partition to store the message in. 事件中心使用分区实现可伸缩性,并且需要至少两个分区。Event Hubs uses partitions to enable scalability and require a minimum of two. 只保证一个分区内的消息依次传递。The ordered delivery of messages is only guaranteed within a partition. 如果未指明事件中心要在哪个分区中放置消息,它将使用循环算法来分配负载。If we do not instruct Event Hub in which partition to place the message, it uses a round-robin algorithm to distribute the load. 但是,这可能会导致一些消息不按顺序处理。However, that may cause some of our messages to be processed out of order.

分区Partitions

为了确保消息依次传递给使用者并使用分区的负载分配功能,我已选择将 HTTP 请求消息发送到一个分区,将 HTTP 响应消息发送到另一个分区。To ensure our messages are delivered to consumers in order and take advantage of the load distribution capability of partitions, I chose to send HTTP request messages to one partition and HTTP response messages to a second partition. 这可以确保负载平均分配,并且按顺序使用所有请求和所有响应。This ensures an even load distribution and we can guarantee that all requests will be consumed in order and all responses are consumed in order. 响应有可能在相应请求之前使用,但这不成问题,因为我们有不同的机制能使请求与响应相互关联,并且我们知道请求始终出现在响应之前。It is possible for a response to be consumed before the corresponding request, but as that is not a problem as we have a different mechanism for correlating requests to responses and we know that requests always come before responses.

HTTP 有效负载HTTP payloads

在构建 requestLine 之后,请查看是否应截断请求正文。After building the requestLine, we check to see if the request body should be truncated. 请求正文被截断成只有 1024 个字符。The request body is truncated to only 1024. 可以增大此值,不过单个事件中心消息受限于 256 KB,因此有些 HTTP 消息正文可能无法放入单个消息。This could be increased, however individual Event Hub messages are limited to 256 KB, so it is likely that some HTTP message bodies will not fit in a single message. 执行日志记录和分析时,可以从 HTTP 请求行与标头派生大量信息。When doing logging and analytics a significant amount of information can be derived from just the HTTP request line and headers. 此外,许多 API 请求只返回小型正文,因此相比于降低传输、处理和存储成本来保留所有正文内容,截断大型正文所造成的信息价值损失相当微小。Also, many APIs request only return small bodies and so the loss of information value by truncating large bodies is fairly minimal in comparison to the reduction in transfer, processing, and storage costs to keep all body contents. 有关处理正文的最后一个注意事项是,需要将 true 传递给 As<string>() 方法,因为虽然我们可以读取正文内容,但也希望后端 API 能够读取正文。One final note about processing the body is that we need to pass true to the As<string>() method because we are reading the body contents, but was also wanted the backend API to be able to read the body. 将 true 传递给此方法后,正文会缓冲,以便进行第二次读取。By passing true to this method, we cause the body to be buffered so that it can be read a second time. 必须要注意 API 是否上传极大型文件或使用很长的轮询。This is important to be aware of if you have an API that does uploading of large files or uses long polling. 在这种情况下,最好完全避免读取正文。In these cases, it would be best to avoid reading the body at all.

HTTP 标头HTTP headers

HTTP 标头可以转换为采用简单键/值对格式的消息格式。HTTP Headers can be transferred over into the message format in a simple key/value pair format. 我们已选择去除某些安全机密字段,以免不必要地泄漏凭据信息。We have chosen to strip out certain security sensitive fields, to avoid unnecessarily leaking credential information. API 密钥和其他凭据不太可能用于分析。It is unlikely that API keys and other credentials would be used for analytics purposes. 如果想要分析用户及其使用的特定产品,可以从 context 对象获取这些信息,然后将其添加到消息。If we wish to do analysis on the user and the particular product they are using, then we could get that from the context object and add that to the message.

消息元数据Message Metadata

创建要发送到事件中心的完整消息时,第一行实际上不是 application/http 消息的一部分。When building the complete message to send to the event hub, the first line is not actually part of the application/http message. 第一行是附加的元数据,其中包括消息是请求消息还是响应消息,以及使响应与请求相互关联的消息 ID。The first line is additional metadata consisting of whether the message is a request or response message and a message ID, which is used to correlate requests to responses. 使用如下所示的另一个策略可以创建消息 ID:The message ID is created by using another policy that looks like this:

<set-variable name="message-id" value="@(Guid.NewGuid())" />

应事先创建请求消息,将它存储在变量中,直到返回响应为止,然后将请求和响应作为单个消息发送。We could have created the request message, stored that in a variable until the response was returned and then sent the request and response as a single message. 但是,独立发送请求和响应并使用消息 ID 使两者相互关联,可以在消息大小方面获得更大的弹性,此外,能够使用多个分区并保留消息顺序,使请求更快出现在日志记录仪表板中。However, by sending the request and response independently and using a message id to correlate the two, we get a bit more flexibility in the message size, the ability to take advantage of multiple partitions whilst maintaining message order and the request will appear in our logging dashboard sooner. 在某些情况下,有效的响应也可能永远不会发送到事件中心,原因可能是 API 管理服务发生严重请求错误,但我们仍保留了该请求的记录。There also may be some scenarios where a valid response is never sent to the event hub, possibly due to a fatal request error in the API Management service, but we still have a record of the request.

用于发送响应 HTTP 消息的策略看起来与请求非常类似。完整的策略配置如下所示:The policy to send the response HTTP message looks similar to the request and so the complete policy configuration looks like this:

<policies>
  <inbound>
      <set-variable name="message-id" value="@(Guid.NewGuid())" />
      <log-to-eventhub logger-id="conferencelogger" partition-id="0">
      @{
          var requestLine = string.Format("{0} {1} HTTP/1.1\r\n",
                                                      context.Request.Method,
                                                      context.Request.Url.Path + context.Request.Url.QueryString);

          var body = context.Request.Body?.As<string>(true);
          if (body != null && body.Length > 1024)
          {
              body = body.Substring(0, 1024);
          }

          var headers = context.Request.Headers
                               .Where(h => h.Key != "Authorization" && h.Key != "Ocp-Apim-Subscription-Key")
                               .Select(h => string.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))
                               .ToArray<string>();

          var headerString = (headers.Any()) ? string.Join("\r\n", headers) + "\r\n" : string.Empty;

          return "request:"   + context.Variables["message-id"] + "\n"
                              + requestLine + headerString + "\r\n" + body;
      }
  </log-to-eventhub>
  </inbound>
  <backend>
      <forward-request follow-redirects="true" />
  </backend>
  <outbound>
      <log-to-eventhub logger-id="conferencelogger" partition-id="1">
      @{
          var statusLine = string.Format("HTTP/1.1 {0} {1}\r\n",
                                              context.Response.StatusCode,
                                              context.Response.StatusReason);

          var body = context.Response.Body?.As<string>(true);
          if (body != null && body.Length > 1024)
          {
              body = body.Substring(0, 1024);
          }

          var headers = context.Response.Headers
                                          .Select(h => string.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))
                                          .ToArray<string>();

          var headerString = (headers.Any()) ? string.Join("\r\n", headers) + "\r\n" : string.Empty;

          return "response:"  + context.Variables["message-id"] + "\n"
                              + statusLine + headerString + "\r\n" + body;
     }
  </log-to-eventhub>
  </outbound>
</policies>

set-variable 策略创建一个可供 <inbound> 节和 <outbound> 节中的 log-to-eventhub 策略访问的值。The set-variable policy creates a value that is accessible by both the log-to-eventhub policy in the <inbound> section and the <outbound> section.

从事件中心接收事件Receiving events from Event Hubs

使用 AMQP 协议可从 Azure 事件中心接收事件。Events from Azure Event Hub are received using the AMQP protocol. Microsoft 服务总线团队提供了客户端库来方便使用事件。The Microsoft Service Bus team have made client libraries available to make the consuming events easier. 支持两种不同的方法:一种是成为直接使用者,另一种是使用 EventProcessorHost 类。There are two different approaches supported, one is being a Direct Consumer and the other is using the EventProcessorHost class. 事件中心编程指南中可以找到这两种方法的示例。Examples of these two approaches can be found in the Event Hubs Programming Guide. 简而言之,两者的差别在于:Direct Consumer 提供给完全控制权,而 EventProcessorHost 可以自动完成一些繁琐的工作,但在如何处理这些事件上做出了假设。The short version of the differences is, Direct Consumer gives you complete control and the EventProcessorHost does some of the plumbing work for you but makes certain assumptions about how you process those events.

EventProcessorHostEventProcessorHost

为方便起见,本示例将使用 EventProcessorHost,但这不一定是此特定方案的最佳选择。In this sample, we use the EventProcessorHost for simplicity, however it may not the best choice for this particular scenario. EventProcessorHost 努力确保用户无需担心特定事件处理器类中发生线程问题。EventProcessorHost does the hard work of making sure you don't have to worry about threading issues within a particular event processor class. 但是,在我们的方案中,只需将消息转换为另一种格式,并使用异步方法将它传递到另一个服务。However, in our scenario, we are simply converting the message to another format and passing it along to another service using an async method. 不需要更新共享状态,因此没有线程问题的风险。There is no need for updating shared state and therefore no risk of threading issues. 在大多数情况下,EventProcessorHost 可能是最佳选择,当然也是更方便的选项。For most scenarios, EventProcessorHost is probably the best choice and it is certainly the easier option.

IEventProcessorIEventProcessor

使用 EventProcessorHost 时的核心概念是创建包含 ProcessEventAsync 方法的 IEventProcessor 接口的实现。The central concept when using EventProcessorHost is to create an implementation of the IEventProcessor interface, which contains the method ProcessEventAsync. 该方法的基本构成如下所示:The essence of that method is shown here:

async Task IEventProcessor.ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
{

   foreach (EventData eventData in messages)
   {
       _Logger.LogInfo(string.Format("Event received from partition: {0} - {1}", context.Lease.PartitionId,eventData.PartitionKey));

       try
       {
           var httpMessage = HttpMessage.Parse(eventData.GetBodyStream());
           await _MessageContentProcessor.ProcessHttpMessage(httpMessage);
       }
       catch (Exception ex)
       {
           _Logger.LogError(ex.Message);
       }
   }
    ... checkpointing code snipped ...
}

EventData 对象的列表将传递到此方法,我们需要迭代该列表。A list of EventData objects are passed into the method and we iterate over that list. 每个方法的字节将分析成 HttpMessage 对象,该对象将传递到 IHttpMessageProcessor 的实例。The bytes of each method are parsed into an HttpMessage object and that object is passed to an instance of IHttpMessageProcessor.

HttpMessageHttpMessage

HttpMessage 实例包含三个数据片段:The HttpMessage instance contains three pieces of data:

public class HttpMessage
{
   public Guid MessageId { get; set; }
   public bool IsRequest { get; set; }
   public HttpRequestMessage HttpRequestMessage { get; set; }
   public HttpResponseMessage HttpResponseMessage { get; set; }

... parsing code snipped ...

}

HttpMessage 实例包含一个用于将 HTTP 请求连接到相应 HTTP 响应的 MessageId GUID,以及一个用于确定对象是否包含 HttpRequestMessage 和 HttpResponseMessage 实例的布尔值。The HttpMessage instance contains a MessageId GUID that allows us to connect the HTTP request to the corresponding HTTP response and a boolean value that identifies if the object contains an instance of a HttpRequestMessage and HttpResponseMessage. System.Net.Http 使用内置 HTTP 类,可以利用 System.Net.Http.Formatting 中包含的 application/http 分析代码。By using the built in HTTP classes from System.Net.Http, I was able to take advantage of the application/http parsing code that is included in System.Net.Http.Formatting.

IHttpMessageProcessorIHttpMessageProcessor

然后,HttpMessage 实例将转发到 IHttpMessageProcessor 的实现,这是为了分离从 Azure 事件中心接收和解释事件以及实际处理事件而创建的接口。The HttpMessage instance is then forwarded to implementation of IHttpMessageProcessor, which is an interface I created to decouple the receiving and interpretation of the event from Azure Event Hub and the actual processing of it.

转发 HTTP 消息Forwarding the HTTP message

对于此示例,我认为将 HTTP 请求推送到 Moesif API 分析会很有趣。For this sample, I decided it would be interesting to push the HTTP Request over to Moesif API Analytics. Moesif 是专门用于 HTTP 分析和调试的基于云的服务。Moesif is a cloud based service that specializes in HTTP analytics and debugging. 该服务提供免费层,因此可以方便试用。它可以让我们实时查看流经 API 管理服务的 HTTP 请求。They have a free tier, so it is easy to try and it allows us to see the HTTP requests in real-time flowing through our API Management service.

IHttpMessageProcessor 实现如下所示:The IHttpMessageProcessor implementation looks like this,

public class MoesifHttpMessageProcessor : IHttpMessageProcessor
{
    private readonly string RequestTimeName = "MoRequestTime";
    private MoesifApiClient _MoesifClient;
    private ILogger _Logger;
    private string _SessionTokenKey;
    private string _ApiVersion;
    public MoesifHttpMessageProcessor(ILogger logger)
    {
        var appId = Environment.GetEnvironmentVariable("APIMEVENTS-MOESIF-APP-ID", EnvironmentVariableTarget.Process);
        _MoesifClient = new MoesifApiClient(appId);
        _SessionTokenKey = Environment.GetEnvironmentVariable("APIMEVENTS-MOESIF-SESSION-TOKEN", EnvironmentVariableTarget.Process);
        _ApiVersion = Environment.GetEnvironmentVariable("APIMEVENTS-MOESIF-API-VERSION", EnvironmentVariableTarget.Process);
        _Logger = logger;
    }

    public async Task ProcessHttpMessage(HttpMessage message)
    {
        if (message.IsRequest)
        {
            message.HttpRequestMessage.Properties.Add(RequestTimeName, DateTime.UtcNow);
            return;
        }

        EventRequestModel moesifRequest = new EventRequestModel()
        {
            Time = (DateTime) message.HttpRequestMessage.Properties[RequestTimeName],
            Uri = message.HttpRequestMessage.RequestUri.OriginalString,
            Verb = message.HttpRequestMessage.Method.ToString(),
            Headers = ToHeaders(message.HttpRequestMessage.Headers),
            ApiVersion = _ApiVersion,
            IpAddress = null,
            Body = message.HttpRequestMessage.Content != null ? System.Convert.ToBase64String(await message.HttpRequestMessage.Content.ReadAsByteArrayAsync()) : null,
            TransferEncoding = "base64"
        };

        EventResponseModel moesifResponse = new EventResponseModel()
        {
            Time = DateTime.UtcNow,
            Status = (int) message.HttpResponseMessage.StatusCode,
            IpAddress = Environment.MachineName,
            Headers = ToHeaders(message.HttpResponseMessage.Headers),
            Body = message.HttpResponseMessage.Content != null ? System.Convert.ToBase64String(await message.HttpResponseMessage.Content.ReadAsByteArrayAsync()) : null,
            TransferEncoding = "base64"
        };

        Dictionary<string, string> metadata = new Dictionary<string, string>();
        metadata.Add("ApimMessageId", message.MessageId.ToString());

        EventModel moesifEvent = new EventModel()
        {
            Request = moesifRequest,
            Response = moesifResponse,
            SessionToken = _SessionTokenKey != null ? message.HttpRequestMessage.Headers.GetValues(_SessionTokenKey).FirstOrDefault() : null,
            Tags = null,
            UserId = null,
            Metadata = metadata
        };

        Dictionary<string, string> response = await _MoesifClient.Api.CreateEventAsync(moesifEvent);

        _Logger.LogDebug("Message forwarded to Moesif");
    }

    private static Dictionary<string, string> ToHeaders(HttpHeaders headers)
    {
        IEnumerable<KeyValuePair<string, IEnumerable<string>>> enumerable = headers.GetEnumerator().ToEnumerable();
        return enumerable.ToDictionary(p => p.Key, p => p.Value.GetEnumerator()
                                                         .ToEnumerable()
                                                         .ToList()
                                                         .Aggregate((i, j) => i + ", " + j));
    }
}

MoesifHttpMessageProcessor 利用可轻松将 HTTP 事件数据推送到其服务的适用于 Moesif 的 C# API 库The MoesifHttpMessageProcessor takes advantage of a C# API library for Moesif that makes it easy to push HTTP event data into their service. 若要将 HTTP 数据发送到 Moesif 收集器 API,需要拥有帐户和应用程序 ID。可通过在 Moesif 网站上创建帐户,然后转到_右上方菜单_ -> “应用设置”来获取 Moesif 应用程序 ID。In order to send HTTP data to the Moesif Collector API, you need an account and an Application Id. You get a Moesif Application Id by creating an account on Moesif's website and then go to the Top Right Menu -> App Setup.

完整示例Complete sample

GitHub 上提供了本示例的源代码和测试。The source code and tests for the sample are on GitHub. 需要准备好 API 管理服务连接的事件中心存储帐户才能自行运行本示例。You need an API Management Service, a connected Event Hub, and a Storage Account to run the sample for yourself.

本示例只是一个简单的控制台应用程序,可以侦听来自事件中心的事件,将其转换为 Moesif EventRequestModelEventResponseModel 对象,然后将这些对象转发到 Moesif 收集器 API。The sample is just a simple Console application that listens for events coming from Event Hub, converts them into a Moesif EventRequestModel and EventResponseModel objects and then forwards them on to the Moesif Collector API.

在下面的动画中,可以看到在开发人员门户对 API 发出的请求,显示正在接收、处理和转发消息的控制台应用程序,以及随后在事件流中显示的请求和响应。In the following animated image, you can see a request being made to an API in the Developer Portal, the Console application showing the message being received, processed, and forwarded and then the request and response showing up in the Event Stream.

演示如何将请求转发到 Runscope

摘要Summary

Azure API 管理服务提供了一个理想位置用于捕获 API 的双向 HTTP 流量。Azure API Management service provides an ideal place to capture the HTTP traffic traveling to and from your APIs. Azure 事件中心是一个高度可缩放的、低成本的解决方案,可以捕获流量并将其馈送到辅助处理系统进行日志记录、监视和其他复杂分析。Azure Event Hubs is a highly scalable, low-cost solution for capturing that traffic and feeding it into secondary processing systems for logging, monitoring, and other sophisticated analytics. 只需编写几十行代码,就能轻松连接到 Moesif 等第三方流量监视系统。Connecting to third-party traffic monitoring systems like Moesif is as simple as a few dozen lines of code.

后续步骤Next steps