2014-05-07 3 views
0

J'utilise un ancien projet qui fait toujours référence à RestServiceBase<TRequest> et je sais que je dois enregistrer toutes les demandes et réponses d'appels pour cette API.Demande et réponse de RestServiceBase de journal

Je peux facilement passer par chaque implémentation de service et ajouter quelque chose comme:

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

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

et après une extraction et l'insertion dans la base de données que je recevrais un journal qui ressemble à:

enter image description here

see larger image

Mais je me demandais s'il y a déjà une implémentation Logging que je pouvais profiter et peut facilement brancher à la classe de base et connectez-vous automatiquement, comme je voudrais vous connecter à l'appel ainsi Auth (pour obtenir le username en cours d'authentification et faire correspondre le username avec le session.

Répondre

2

Bien qu'il est construit dans l'exploitation forestière à l'aide du RequestLogsFeature, cette action vise à déboguer votre projet. Cependant, ServiceStack facilite l'obtention de toutes les informations pertinentes dont vous avez besoin en utilisant un filtre de requête qui sera exécuté pour chaque requête.

Le code ci-dessous montre comment vous enregistrer combien de temps une demande prend, et les autres valeurs que vous recherchez, comme SessionId, adresse IP, Verbe, URI, etc.

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

J'espère que cela aide.

+0

Cela m'a beaucoup aidé, même si, comme je l'ai dit dans la première ligne, je n'utilise pas la dernière version et pour cela je n'ai pas 'GlobalResponseFilters' /' GlobalRequestFilters' mais je suis similaire ... dans ma version Je n'ai pas accès à 'req.Dto' car il ne fait pas partie des propriétés de l'objet' req', mais j'espère que 'req.InputStream' aura quelque chose si ce n'est pas' GET'. Dans l'ensemble, merci un million! – balexandre

+0

Il prend en charge la journalisation des réponses, consultez l'interface IrequestLogger, https: // github.com/ServiceStack/ServiceStack/blob/v3/src/ServiceStack.Interfaces/ServiceHost/IRequestLogger.cs. Nous avons créé un databaseRequestLogger pour persister en db et choisi de ne pas sauvegarder les réponses afin de ne pas gonfler la base de données. – BrandonG

+0

@BrandonG Merci. Je suis corrigé à ce sujet, j'ai supprimé cette ligne. J'aime mettre en œuvre la journalisation à travers les filtres de requête car elle permet un contrôle précis des valeurs log-able, telles que des en-têtes spécifiques, etc. J'apprécierais votre réponse si vous avez fourni une implémentation. – Scott

0

Vous pouvez enregistrer toutes les demandes/réponses en utilisant le RequestLogsFeature. Dans mon entreprise, un collègue a mis en place la possibilité de sauvegarder cette information dans une base de données. Nous n'enregistrons cependant pas les réponses.

Mise à jour Quelqu'un a demandé notre implémentation. À l'origine, nous utilisions NewtonSoft.Json pour json mais je ne pouvais jamais voir les données de demande dans l'interface utilisateur car elles étaient désérialisées. Donc je suis passé à Servicestack json et cela a fonctionné comme prévu. Ici, il est:

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(); 
        } 
       } 
      } 
     } 
    }