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