Extend 'slow' request logging to other server outbound requests (forms, rest, async rest) as well as the existing logging on outbound OSD requests.

Also prints out the first 100 chars of any slow request data since this can contain useful info (such as agent ID).
0.7.3-extended
Justin Clark-Casey (justincc) 2012-05-04 01:12:56 +01:00
parent e0e63f312f
commit 59e93b8ee3
1 changed files with 202 additions and 112 deletions

View File

@ -53,45 +53,36 @@ namespace OpenSim.Framework
LogManager.GetLogger( LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType); MethodBase.GetCurrentMethod().DeclaringType);
private static int m_requestNumber = 0; /// <summary>
/// Request number for diagnostic purposes.
/// </summary>
public static int RequestNumber = 0;
// this is the header field used to communicate the local request id /// <summary>
// used for performance and debugging /// this is the header field used to communicate the local request id
/// used for performance and debugging
/// </summary>
public const string OSHeaderRequestID = "opensim-request-id"; public const string OSHeaderRequestID = "opensim-request-id";
// number of milliseconds a call can take before it is considered /// <summary>
// a "long" call for warning & debugging purposes /// Number of milliseconds a call can take before it is considered
/// a "long" call for warning & debugging purposes
/// </summary>
public const int LongCallTime = 500; public const int LongCallTime = 500;
// /// <summary> /// <summary>
// /// Send LLSD to an HTTP client in application/llsd+json form /// The maximum length of any data logged because of a long request time.
// /// </summary> /// </summary>
// /// <param name="response">HTTP response to send the data in</param> /// <remarks>
// /// <param name="body">LLSD to send to the client</param> /// This is to truncate any really large post data, such as an asset. In theory, the first section should
// public static void SendJSONResponse(OSHttpResponse response, OSDMap body) /// give us useful information about the call (which agent it relates to if applicable, etc.).
// { /// </remarks>
// byte[] responseData = Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(body)); public const int MaxRequestDiagLength = 100;
//
// response.ContentEncoding = Encoding.UTF8; /// <summary>
// response.ContentLength = responseData.Length; /// Dictionary of end points
// response.ContentType = "application/llsd+json"; /// </summary>
// response.Body.Write(responseData, 0, responseData.Length); private static Dictionary<string,object> m_endpointSerializer = new Dictionary<string,object>();
// }
//
// /// <summary>
// /// Send LLSD to an HTTP client in application/llsd+xml form
// /// </summary>
// /// <param name="response">HTTP response to send the data in</param>
// /// <param name="body">LLSD to send to the client</param>
// public static void SendXMLResponse(OSHttpResponse response, OSDMap body)
// {
// byte[] responseData = OSDParser.SerializeLLSDXmlBytes(body);
//
// response.ContentEncoding = Encoding.UTF8;
// response.ContentLength = responseData.Length;
// response.ContentType = "application/llsd+xml";
// response.Body.Write(responseData, 0, responseData.Length);
// }
/// <summary> /// <summary>
/// Make a GET or GET-like request to a web service that returns LLSD /// Make a GET or GET-like request to a web service that returns LLSD
@ -166,12 +157,14 @@ namespace OpenSim.Framework
public static OSDMap ServiceOSDRequest(string url, OSDMap data, string method, int timeout, bool compressed) public static OSDMap ServiceOSDRequest(string url, OSDMap data, string method, int timeout, bool compressed)
{ {
int reqnum = m_requestNumber++; int reqnum = RequestNumber++;
// m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method); // m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
string errorMessage = "unknown error"; string errorMessage = "unknown error";
int tickstart = Util.EnvironmentTickCount(); int tickstart = Util.EnvironmentTickCount();
int tickdata = 0; int tickdata = 0;
string strBuffer = null;
try try
{ {
@ -186,7 +179,7 @@ namespace OpenSim.Framework
// If there is some input, write it into the request // If there is some input, write it into the request
if (data != null) if (data != null)
{ {
string strBuffer = OSDParser.SerializeJsonString(data); strBuffer = OSDParser.SerializeJsonString(data);
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(strBuffer); byte[] buffer = System.Text.Encoding.UTF8.GetBytes(strBuffer);
if (compressed) if (compressed)
@ -246,11 +239,18 @@ namespace OpenSim.Framework
} }
finally finally
{ {
// This just dumps a warning for any operation that takes more than 100 ms
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > LongCallTime) if (tickdiff > LongCallTime)
m_log.DebugFormat("[WEB UTIL]: osd request <{0}> (URI:{1}, METHOD:{2}) took {3}ms overall, {4}ms writing", m_log.InfoFormat(
reqnum,url,method,tickdiff,tickdata); "[OSD REQUEST]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
method,
url,
tickdiff,
tickdata,
strBuffer != null
? (strBuffer.Length > MaxRequestDiagLength ? strBuffer.Remove(MaxRequestDiagLength) : strBuffer)
: "");
} }
m_log.DebugFormat( m_log.DebugFormat(
@ -316,17 +316,17 @@ namespace OpenSim.Framework
public static OSDMap ServiceFormRequest(string url, NameValueCollection data, int timeout) public static OSDMap ServiceFormRequest(string url, NameValueCollection data, int timeout)
{ {
int reqnum = m_requestNumber++; int reqnum = RequestNumber++;
string method = (data != null && data["RequestMethod"] != null) ? data["RequestMethod"] : "unknown"; string method = (data != null && data["RequestMethod"] != null) ? data["RequestMethod"] : "unknown";
// m_log.DebugFormat("[WEB UTIL]: <{0}> start form request for {1}, method {2}",reqnum,url,method); // m_log.DebugFormat("[WEB UTIL]: <{0}> start form request for {1}, method {2}",reqnum,url,method);
string errorMessage = "unknown error"; string errorMessage = "unknown error";
int tickstart = Util.EnvironmentTickCount(); int tickstart = Util.EnvironmentTickCount();
int tickdata = 0; int tickdata = 0;
string queryString = null;
try try
{ {
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.Method = "POST"; request.Method = "POST";
request.Timeout = timeout; request.Timeout = timeout;
@ -337,7 +337,7 @@ namespace OpenSim.Framework
if (data != null) if (data != null)
{ {
string queryString = BuildQueryString(data); queryString = BuildQueryString(data);
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(queryString); byte[] buffer = System.Text.Encoding.UTF8.GetBytes(queryString);
request.ContentLength = buffer.Length; request.ContentLength = buffer.Length;
@ -380,11 +380,19 @@ namespace OpenSim.Framework
{ {
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > LongCallTime) if (tickdiff > LongCallTime)
m_log.InfoFormat("[WEB UTIL]: form request <{0}> (URI:{1}, METHOD:{2}) took {3}ms overall, {4}ms writing", m_log.InfoFormat(
reqnum,url,method,tickdiff,tickdata); "[SERVICE FORM]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
method,
url,
tickdiff,
tickdata,
queryString != null
? (queryString.Length > MaxRequestDiagLength) ? queryString.Remove(MaxRequestDiagLength) : queryString
: "");
} }
m_log.WarnFormat("[WEB UTIL]: <{0}> form request to {1} failed: {2}", reqnum, url, errorMessage); m_log.WarnFormat("[SERVICE FORM]: <{0}> form request to {1} failed: {2}", reqnum, url, errorMessage);
return ErrorResponseMap(errorMessage); return ErrorResponseMap(errorMessage);
} }
@ -658,8 +666,6 @@ namespace OpenSim.Framework
return new string[0]; return new string[0];
} }
} }
public static class AsynchronousRestObjectRequester public static class AsynchronousRestObjectRequester
@ -682,6 +688,12 @@ namespace OpenSim.Framework
public static void MakeRequest<TRequest, TResponse>(string verb, public static void MakeRequest<TRequest, TResponse>(string verb,
string requestUrl, TRequest obj, Action<TResponse> action) string requestUrl, TRequest obj, Action<TResponse> action)
{ {
int reqnum = WebUtil.RequestNumber++;
// m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
int tickstart = Util.EnvironmentTickCount();
int tickdata = 0;
// m_log.DebugFormat("[ASYNC REQUEST]: Starting {0} {1}", verb, requestUrl); // m_log.DebugFormat("[ASYNC REQUEST]: Starting {0} {1}", verb, requestUrl);
Type type = typeof(TRequest); Type type = typeof(TRequest);
@ -692,12 +704,13 @@ namespace OpenSim.Framework
XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
request.Method = verb; request.Method = verb;
MemoryStream buffer = null;
if (verb == "POST") if (verb == "POST")
{ {
request.ContentType = "text/xml"; request.ContentType = "text/xml";
MemoryStream buffer = new MemoryStream(); buffer = new MemoryStream();
XmlWriterSettings settings = new XmlWriterSettings(); XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8; settings.Encoding = Encoding.UTF8;
@ -719,6 +732,9 @@ namespace OpenSim.Framework
requestStream.Write(buffer.ToArray(), 0, length); requestStream.Write(buffer.ToArray(), 0, length);
requestStream.Close(); requestStream.Close();
// capture how much time was spent writing
tickdata = Util.EnvironmentTickCountSubtract(tickstart);
request.BeginGetResponse(delegate(IAsyncResult ar) request.BeginGetResponse(delegate(IAsyncResult ar)
{ {
response = request.EndGetResponse(ar); response = request.EndGetResponse(ar);
@ -744,88 +760,108 @@ namespace OpenSim.Framework
}, null); }, null);
}, null); }, null);
return;
} }
else
request.BeginGetResponse(delegate(IAsyncResult res2)
{ {
try request.BeginGetResponse(delegate(IAsyncResult res2)
{ {
// If the server returns a 404, this appears to trigger a System.Net.WebException even though that isn't
// documented in MSDN
response = request.EndGetResponse(res2);
Stream respStream = null;
try try
{ {
respStream = response.GetResponseStream(); // If the server returns a 404, this appears to trigger a System.Net.WebException even though that isn't
deserial = (TResponse)deserializer.Deserialize(respStream); // documented in MSDN
} response = request.EndGetResponse(res2);
catch (System.InvalidOperationException)
{ Stream respStream = null;
} try
finally
{
respStream.Close();
response.Close();
}
}
catch (WebException e)
{
if (e.Status == WebExceptionStatus.ProtocolError)
{
if (e.Response is HttpWebResponse)
{ {
HttpWebResponse httpResponse = (HttpWebResponse)e.Response; respStream = response.GetResponseStream();
deserial = (TResponse)deserializer.Deserialize(respStream);
if (httpResponse.StatusCode != HttpStatusCode.NotFound) }
{ catch (System.InvalidOperationException)
// We don't appear to be handling any other status codes, so log these feailures to that {
// people don't spend unnecessary hours hunting phantom bugs. }
m_log.DebugFormat( finally
"[ASYNC REQUEST]: Request {0} {1} failed with unexpected status code {2}", {
verb, requestUrl, httpResponse.StatusCode); respStream.Close();
} response.Close();
} }
} }
else catch (WebException e)
{
if (e.Status == WebExceptionStatus.ProtocolError)
{
if (e.Response is HttpWebResponse)
{
HttpWebResponse httpResponse = (HttpWebResponse)e.Response;
if (httpResponse.StatusCode != HttpStatusCode.NotFound)
{
// We don't appear to be handling any other status codes, so log these feailures to that
// people don't spend unnecessary hours hunting phantom bugs.
m_log.DebugFormat(
"[ASYNC REQUEST]: Request {0} {1} failed with unexpected status code {2}",
verb, requestUrl, httpResponse.StatusCode);
}
}
}
else
{
m_log.ErrorFormat(
"[ASYNC REQUEST]: Request {0} {1} failed with status {2} and message {3}",
verb, requestUrl, e.Status, e.Message);
}
}
catch (Exception e)
{ {
m_log.ErrorFormat( m_log.ErrorFormat(
"[ASYNC REQUEST]: Request {0} {1} failed with status {2} and message {3}", "[ASYNC REQUEST]: Request {0} {1} failed with exception {2}{3}",
verb, requestUrl, e.Status, e.Message); verb, requestUrl, e.Message, e.StackTrace);
} }
}
catch (Exception e) // m_log.DebugFormat("[ASYNC REQUEST]: Received {0}", deserial.ToString());
try
{
action(deserial);
}
catch (Exception e)
{
m_log.ErrorFormat(
"[ASYNC REQUEST]: Request {0} {1} callback failed with exception {2}{3}",
verb, requestUrl, e.Message, e.StackTrace);
}
}, null);
}
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > WebUtil.LongCallTime)
{
string originalRequest = null;
if (buffer != null)
{ {
m_log.ErrorFormat( originalRequest = Encoding.UTF8.GetString(buffer.ToArray());
"[ASYNC REQUEST]: Request {0} {1} failed with exception {2}{3}",
verb, requestUrl, e.Message, e.StackTrace); if (originalRequest.Length > WebUtil.MaxRequestDiagLength)
originalRequest = originalRequest.Remove(WebUtil.MaxRequestDiagLength);
} }
// m_log.DebugFormat("[ASYNC REQUEST]: Received {0}", deserial.ToString()); m_log.InfoFormat(
"[ASYNC REQUEST]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
try reqnum,
{ verb,
action(deserial); requestUrl,
} tickdiff,
catch (Exception e) tickdata,
{ originalRequest);
m_log.ErrorFormat( }
"[ASYNC REQUEST]: Request {0} {1} callback failed with exception {2}{3}",
verb, requestUrl, e.Message, e.StackTrace);
}
}, null);
} }
} }
public static class SynchronousRestFormsRequester public static class SynchronousRestFormsRequester
{ {
private static readonly ILog m_log = private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
/// <summary> /// <summary>
/// Perform a synchronous REST request. /// Perform a synchronous REST request.
@ -839,6 +875,12 @@ namespace OpenSim.Framework
/// the request. You'll want to make sure you deal with this as they're not uncommon</exception> /// the request. You'll want to make sure you deal with this as they're not uncommon</exception>
public static string MakeRequest(string verb, string requestUrl, string obj) public static string MakeRequest(string verb, string requestUrl, string obj)
{ {
int reqnum = WebUtil.RequestNumber++;
// m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
int tickstart = Util.EnvironmentTickCount();
int tickdata = 0;
WebRequest request = WebRequest.Create(requestUrl); WebRequest request = WebRequest.Create(requestUrl);
request.Method = verb; request.Method = verb;
string respstring = String.Empty; string respstring = String.Empty;
@ -874,6 +916,9 @@ namespace OpenSim.Framework
{ {
if (requestStream != null) if (requestStream != null)
requestStream.Close(); requestStream.Close();
// capture how much time was spent writing
tickdata = Util.EnvironmentTickCountSubtract(tickstart);
} }
} }
@ -912,6 +957,18 @@ namespace OpenSim.Framework
m_log.DebugFormat("[FORMS]: InvalidOperationException on receiving {0} {1}", verb, requestUrl); m_log.DebugFormat("[FORMS]: InvalidOperationException on receiving {0} {1}", verb, requestUrl);
} }
} }
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > WebUtil.LongCallTime)
m_log.InfoFormat(
"[FORMS]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
verb,
requestUrl,
tickdiff,
tickdata,
obj.Length > WebUtil.MaxRequestDiagLength ? obj.Remove(WebUtil.MaxRequestDiagLength) : obj);
return respstring; return respstring;
} }
} }
@ -934,17 +991,24 @@ namespace OpenSim.Framework
/// the request. You'll want to make sure you deal with this as they're not uncommon</exception> /// the request. You'll want to make sure you deal with this as they're not uncommon</exception>
public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj) public static TResponse MakeRequest<TRequest, TResponse>(string verb, string requestUrl, TRequest obj)
{ {
int reqnum = WebUtil.RequestNumber++;
// m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method);
int tickstart = Util.EnvironmentTickCount();
int tickdata = 0;
Type type = typeof(TRequest); Type type = typeof(TRequest);
TResponse deserial = default(TResponse); TResponse deserial = default(TResponse);
WebRequest request = WebRequest.Create(requestUrl); WebRequest request = WebRequest.Create(requestUrl);
request.Method = verb; request.Method = verb;
MemoryStream buffer = null;
if ((verb == "POST") || (verb == "PUT")) if ((verb == "POST") || (verb == "PUT"))
{ {
request.ContentType = "text/xml"; request.ContentType = "text/xml";
MemoryStream buffer = new MemoryStream(); buffer = new MemoryStream();
XmlWriterSettings settings = new XmlWriterSettings(); XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8; settings.Encoding = Encoding.UTF8;
@ -977,6 +1041,9 @@ namespace OpenSim.Framework
{ {
if (requestStream != null) if (requestStream != null)
requestStream.Close(); requestStream.Close();
// capture how much time was spent writing
tickdata = Util.EnvironmentTickCountSubtract(tickstart);
} }
} }
@ -1024,6 +1091,29 @@ namespace OpenSim.Framework
verb, requestUrl, e.Message, e.StackTrace); verb, requestUrl, e.Message, e.StackTrace);
} }
int tickdiff = Util.EnvironmentTickCountSubtract(tickstart);
if (tickdiff > WebUtil.LongCallTime)
{
string originalRequest = null;
if (buffer != null)
{
originalRequest = Encoding.UTF8.GetString(buffer.ToArray());
if (originalRequest.Length > WebUtil.MaxRequestDiagLength)
originalRequest = originalRequest.Remove(WebUtil.MaxRequestDiagLength);
}
m_log.InfoFormat(
"[SynchronousRestObjectRequester]: Slow request to <{0}> {1} {2} took {3}ms, {4}ms writing, {5}",
reqnum,
verb,
requestUrl,
tickdiff,
tickdata,
originalRequest);
}
return deserial; return deserial;
} }
} }