2014-05-07 159 views
0

我正在使用仍旧引用RestServiceBase<TRequest>的旧项目,我知道需要记录此API的所有调用请求和响应。Log RestServiceBase请求和响应

我可以很容易地经过每个服务实现,并添加类似:

// get reponse from request 
object response = this.OnGetSpecificAccount(request); 

// log 
Logs.LogRequestWithResponse(
       this.RequestContext, this.Request.HttpMethod, response.ToJson()); 

和一些提取后插入到数据库中,我会得到一个日志看起来像:

enter image description here

see larger image

但我在想,如果有已经有一些日志实现,我可以利用,可以很容易钩到的基类,自动登录这一点,因为我想记录的Auth通话以及(以获取正在验证的username,并将usernamesession相匹配。

回答

2

虽然是建立在使用RequestLogsFeature记录,这是为了调试你的项目。

但是ServiceStack通过使用请求筛选器可以轻松获取所需的所有相关信息,该请求筛选器将针对每个请求运行。

下面的代码演示了如何记录请求需要多长时间,你正在寻找其它的值,如会话ID,IP地址,动词,URI等

public override void Configure(Funq.Container container) 
{ 
    // Record the time the request started. 
    this.GlobalRequestFilters.Add((req, res, requestDto) => req.SetItem("StartTime", DateTime.Now)); 

    // The request has been processed, ready to send response 
    this.GlobalResponseFilters.Add((req, res, responseDto) => { 

     var startTime = req.GetItem("StartTime") as DateTime?; 
     var endTime = DateTime.Now; 

     var log = new { 

      // Request URL 
      Url = req.AbsoluteUri, 

      // Verb 
      Verb = req.Verb, 

      // Session Id 
      SessionId = req.GetSessionId(), 

      // IP Address 
      Ip = req.RemoteIp, 

      // Record the Request DTO 
      Request = req.Dto.ToJson(), 

      // Record the Response DTO 
      Response = responseDto.ToJson(), 

      // Get the start time that was recorded when the request started 
      StartTime = startTime.Value, 

      // Time request finished 
      EndTime = endTime, 

      // Determine the duration of the request 
      Duration = endTime - startTime.Value, 

     }; 

     // Save the log to the database 
     // Resolve the database connection 
     var db = TryResolve<IDbConnectionFactory>().OpenDbConnection(); 
     // ... 

    }); 
} 

Log screenshot

我希望这有助于。

+0

它帮助了很多,但正如我在第一行中所述,我没有使用最新版本,因此我没有'GlobalResponseFilters' /'GlobalRequestFilters',但我有类似的...但在我的版本中我不能访问'req.Dto',因为它不是'req'对象的属性的一部分,但是我希望'req.InputStream'会有一些东西,如果它不是'GET'的话。总而言之,万分感谢! – balexandre

+0

它确实支持响应日志记录,请参阅IrequestLogger接口https:// github。com/ServiceStack/ServiceStack/blob/v3/src/ServiceStack.Interfaces/ServiceHost/IRequestLogger.cs。我们创建了一个databaseRequestLogger来坚持数据库,并选择不保存响应,以免膨胀数据库。 – BrandonG

+0

@BrandonG谢谢。我立场纠正,我已经删除了这条线。我喜欢通过请求过滤器来实现日志记录,因为它提供了对可记录值的细粒度控制,比如特定的头文件等。如果您提供了实现,我将非常感谢您的回答。 – Scott

0

您可以使用RequestLogsFeature来记录所有请求/响应。在我的公司,一位同事实现了将这些信息保存到数据库的能力。然而,我们不记录回应。

更新有人要求我们的实施。我们最初使用的是NewtonSoft.Json for json,但是我无法在UI中看到请求数据,因为它是反序列化的。所以我切换到ServiceStack JSON,并按预期工作。那就是:

DatabaseLoggingFeature.cs

public class DatabaseLoggingFeature : IPlugin 
{ 
    public DatabaseLoggingFeature(string connectionStringKey, int? capacity = null) 
    { 
     AtRestPath = "/requestlogs"; 
     ConnectionStringKey = connectionStringKey; 
     Capacity = capacity; 
     RequiredRoles = new[] {RoleNames.Admin}; 
     EnableErrorTracking = true; 
     EnableRequestBodyTracking = false; 
     ExcludeRequestDtoTypes = new[] {typeof (RequestLogs), typeof (ResourceRequest), typeof (Resources)}; 
     HideRequestBodyForRequestDtoTypes = new[] 
     { 
      typeof (Auth), typeof (Registration) 
     }; 
    } 

    /// <summary> 
    ///  Gets or sets connection string 
    /// </summary> 
    public string ConnectionStringKey { get; set; } 

    /// <summary> 
    ///  RequestLogs service Route, default is /requestlogs 
    /// </summary> 
    public string AtRestPath { get; set; } 

    /// <summary> 
    ///  Turn On/Off Session Tracking 
    /// </summary> 
    public bool EnableSessionTracking { get; set; } 

    /// <summary> 
    ///  Turn On/Off Logging of Raw Request Body, default is Off 
    /// </summary> 
    public bool EnableRequestBodyTracking { get; set; } 

    /// <summary> 
    ///  Turn On/Off Tracking of Responses 
    /// </summary> 
    public bool EnableResponseTracking { get; set; } 

    /// <summary> 
    ///  Turn On/Off Tracking of Exceptions 
    /// </summary> 
    public bool EnableErrorTracking { get; set; } 

    /// <summary> 
    ///  Size of InMemoryRollingRequestLogger circular buffer 
    /// </summary> 
    public int? Capacity { get; set; } 

    /// <summary> 
    ///  Limit access to /requestlogs service to these roles 
    /// </summary> 
    public string[] RequiredRoles { get; set; } 

    /// <summary> 
    ///  Change the RequestLogger provider. Default is InMemoryRollingRequestLogger 
    /// </summary> 
    public IRequestLogger RequestLogger { get; set; } 

    /// <summary> 
    ///  Don't log requests of these types. By default RequestLog's are excluded 
    /// </summary> 
    public Type[] ExcludeRequestDtoTypes { get; set; } 

    /// <summary> 
    ///  Don't log request body's for services with sensitive information. 
    ///  By default Auth and Registration requests are hidden. 
    /// </summary> 
    public Type[] HideRequestBodyForRequestDtoTypes { get; set; } 

    /// <summary> 
    ///  Registers plugin with service stack 
    /// </summary> 
    /// <param name="appHost">Application Host configuration object.</param> 
    public void Register(IAppHost appHost) 
    { 
     appHost.RegisterService<RequestLogsService>(AtRestPath); 

     IRequestLogger requestLogger = RequestLogger ?? new DatabaseRequestLogger(ConnectionStringKey); 
     requestLogger.EnableSessionTracking = EnableSessionTracking; 
     requestLogger.EnableResponseTracking = EnableResponseTracking; 
     requestLogger.EnableRequestBodyTracking = EnableRequestBodyTracking; 
     requestLogger.EnableErrorTracking = EnableErrorTracking; 
     requestLogger.RequiredRoles = RequiredRoles; 
     requestLogger.ExcludeRequestDtoTypes = ExcludeRequestDtoTypes; 
     requestLogger.HideRequestBodyForRequestDtoTypes = HideRequestBodyForRequestDtoTypes; 

     appHost.Register(requestLogger); 

     if (EnableRequestBodyTracking) 
     { 
      appHost.PreRequestFilters.Insert(0, 
       (httpReq, httpRes) => { httpReq.UseBufferedStream = EnableRequestBodyTracking; }); 
     } 
    } 
} 

DatabaseRequestLogger.cs

public class DatabaseRequestLogger : IRequestLogger 
{ 
    /// <summary> 
    ///  Instance of the current database connection; 
    /// </summary> 
    private readonly DbConnection _connection; 

    public DatabaseRequestLogger(string connectionStringKey) 
    { 
     if (string.IsNullOrEmpty(connectionStringKey)) 
     { 
      throw new ArgumentNullException("connectionStringKey"); 
     } 

     if (_connection != null || string.IsNullOrEmpty(connectionStringKey)) return; 
     ConnectionStringSettingsCollection connectionStrings = ConfigurationManager.ConnectionStrings; 

     if (connectionStrings == null) return; 
     foreach (ConnectionStringSettings setting in connectionStrings.Cast<ConnectionStringSettings>() 
      .Where(setting => setting.Name == connectionStringKey)) 
     { 
      ConnectionString = setting.ConnectionString; 
      ProviderName = setting.ProviderName; 
      _connection = GetConnection(ProviderName, ConnectionString); 
     } 
    } 

    public ILog log { get; set; } 

    /// <summary> 
    ///  Gets connection string 
    /// </summary> 
    public string ConnectionString { get; private set; } 

    /// <summary> 
    ///  Gets provider name 
    /// </summary> 
    public string ProviderName { get; private set; } 

    /// <summary> 
    /// </summary> 
    public bool EnableErrorTracking { get; set; } 

    /// <summary> 
    /// </summary> 
    public bool EnableRequestBodyTracking { get; set; } 

    /// <summary> 
    /// </summary> 
    public bool EnableResponseTracking { get; set; } 

    /// <summary> 
    /// </summary> 
    public bool EnableSessionTracking { get; set; } 

    /// <summary> 
    /// </summary> 
    public Type[] ExcludeRequestDtoTypes { get; set; } 

    /// <summary> 
    ///  Returns latest log entries. 
    /// </summary> 
    /// <param name="take">number of records to retrieve.</param> 
    /// <returns>List of RequestLogEntry</returns> 
    public List<RequestLogEntry> GetLatestLogs(int? take) 
    { 
     var entries = new List<RequestLogEntry>(); 

     if (_connection != null) 
     { 
      if (_connection.State == ConnectionState.Closed) 
      { 
       _connection.Open(); 
      } 

      using (DbCommand cmd = _connection.CreateCommand()) 
      { 
       string query = "SELECT {0} FROM [dbo].[RequestLog] ORDER BY LoggedDate DESC"; 

       query = take.HasValue 
        ? string.Format(query, "TOP " + take.Value.ToString(CultureInfo.InvariantCulture) + " * ") 
        : string.Format(query, "*"); 

       cmd.Connection = _connection; 
       cmd.CommandType = CommandType.Text; 
       cmd.CommandText = query; 

       try 
       { 
        DbDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); 

        while (dr.Read()) 
        { 
         var serializer = new JsonSerializer<RequestLogEntry>(); 

         RequestLogEntry entry = serializer.DeserializeFromString(dr["LogEntry"].ToString()); 

         entries.Add(entry); 
        } 
       } 
       catch (Exception) 
       { 
        cmd.Parameters.Clear(); 

        if (cmd.Connection.State == ConnectionState.Open) 
        { 
         cmd.Connection.Close(); 
        } 
       } 
      } 
     } 

     return entries; 
    } 

    /// <summary> 
    /// </summary> 
    public Type[] HideRequestBodyForRequestDtoTypes { get; set; } 

    /// <summary> 
    /// </summary> 
    public string[] RequiredRoles { get; set; } 

    /// <summary> 
    ///  Logs request/response data. 
    /// </summary> 
    /// <param name="requestDto"></param> 
    /// <param name="response">instance of <see cref="IHttpResponse" /> object.</param> 
    /// <param name="requestContext"></param> 
    /// <param name="duration"></param> 
    public void Log(IRequestContext requestContext, object requestDto, object response, TimeSpan duration) 
    { 
     Type requestType = requestDto != null ? requestDto.GetType() : null; 

     // if we don't want to track the request simply bail out. 
     if (ExcludeRequestDtoTypes != null 
      && requestType != null 
      && ExcludeRequestDtoTypes.Contains(requestType)) 
     { 
      return; 
     } 

     //Move this code to another class and create an interface 

     if (_connection != null) 
     { 
      if (_connection.State == ConnectionState.Closed) 
      { 
       _connection.Open(); 
      } 

      using (DbCommand cmd = _connection.CreateCommand()) 
      { 
       cmd.Connection = _connection; 
       cmd.CommandType = CommandType.StoredProcedure; 
       cmd.CommandText = "spLogRequest"; 

       var entry = new RequestLogEntry 
       { 
        DateTime = DateTime.UtcNow, 
        RequestDuration = duration 
       }; 

       IHttpRequest httpRequest = requestContext != null ? requestContext.Get<IHttpRequest>() : null; 

       if (httpRequest != null) 
       { 
        entry.HttpMethod = httpRequest.HttpMethod; 
        entry.AbsoluteUri = httpRequest.AbsoluteUri; 
        entry.PathInfo = httpRequest.PathInfo; 
        entry.IpAddress = requestContext.IpAddress; 
        entry.Headers = httpRequest.Headers.ToDictionary(); 
        entry.Referer = httpRequest.Headers[HttpHeaders.Referer]; 
        entry.ForwardedFor = httpRequest.Headers[HttpHeaders.XForwardedFor]; 
        entry.RequestDto = requestDto; 
        entry.Items = httpRequest.Items; 
        entry.UserAuthId = httpRequest.GetItemOrCookie(HttpHeaders.XUserAuthId); 
        entry.SessionId = httpRequest.GetSessionId(); 
        entry.Session = EnableSessionTracking ? httpRequest.GetSession() : null; 

        if (HideRequestBodyForRequestDtoTypes != null 
         && requestType != null 
         && !HideRequestBodyForRequestDtoTypes.Contains(requestType)) 
        { 
         entry.RequestDto = requestDto; 

         entry.FormData = httpRequest.FormData.ToDictionary(); 

         if (EnableRequestBodyTracking) 
         { 
          entry.RequestBody = httpRequest.GetRawBody(); 
         } 
        } 

        if (!response.IsErrorResponse()) 
        { 
         if (EnableResponseTracking) 
         { 
          entry.ResponseDto = response; 
         } 
        } 
       } 

       DbParameter requestParam = cmd.CreateParameter(); 
       requestParam.ParameterName = "@LogEntry"; 
       requestParam.Direction = ParameterDirection.Input; 
       requestParam.DbType = DbType.String; 
       //JsConfig.IncludeTypeInfo = true; 
       requestParam.Value = new JsonSerializer<RequestLogEntry>().SerializeToString(entry); 

       DbParameter requestDurationParam = cmd.CreateParameter(); 
       requestDurationParam.ParameterName = "@RequestDuration"; 
       requestDurationParam.Direction = ParameterDirection.Input; 
       requestDurationParam.DbType = DbType.Int64; 
       requestDurationParam.Value = entry.RequestDuration.Ticks; 

       cmd.Parameters.Add(requestParam); 
       cmd.Parameters.Add(requestDurationParam); 

       try 
       { 
        cmd.ExecuteNonQuery(); 
        cmd.Connection.Close(); 
       } 
       catch (Exception ex) 
       { 
        log.Error("Error occurred saving request log entry to the database in Log.", ex); 
       } 
       finally 
       { 
        cmd.Parameters.Clear(); 

        if (cmd.Connection.State == ConnectionState.Open) 
        { 
         cmd.Connection.Close(); 
        } 
       } 
      } 
     } 
    }