您可以使用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();
}
}
}
}
}
它帮助了很多,但正如我在第一行中所述,我没有使用最新版本,因此我没有'GlobalResponseFilters' /'GlobalRequestFilters',但我有类似的...但在我的版本中我不能访问'req.Dto',因为它不是'req'对象的属性的一部分,但是我希望'req.InputStream'会有一些东西,如果它不是'GET'的话。总而言之,万分感谢! – balexandre
它确实支持响应日志记录,请参阅IrequestLogger接口https:// github。com/ServiceStack/ServiceStack/blob/v3/src/ServiceStack.Interfaces/ServiceHost/IRequestLogger.cs。我们创建了一个databaseRequestLogger来坚持数据库,并选择不保存响应,以免膨胀数据库。 – BrandonG
@BrandonG谢谢。我立场纠正,我已经删除了这条线。我喜欢通过请求过滤器来实现日志记录,因为它提供了对可记录值的细粒度控制,比如特定的头文件等。如果您提供了实现,我将非常感谢您的回答。 – Scott