Skip to content

Commit 3a25e87

Browse files
authored
Clean Elmah (#7962)
* Strip some information from Elmah logs. * Update to LINQ
1 parent 20c3ac2 commit 3a25e87

1 file changed

Lines changed: 156 additions & 146 deletions

File tree

src/NuGetGallery.Core/Infrastructure/TableErrorLog.cs

Lines changed: 156 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -14,206 +14,216 @@
1414

1515
namespace NuGetGallery.Infrastructure
1616
{
17-
public class ErrorEntity : ITableEntity
17+
public class ErrorEntity : ITableEntity
18+
{
19+
public Error Error { get; set; }
20+
21+
string ITableEntity.ETag
22+
{
23+
get;
24+
set;
25+
}
26+
27+
string ITableEntity.PartitionKey
1828
{
19-
public Error Error { get; set; }
29+
get;
30+
set;
31+
}
32+
33+
string ITableEntity.RowKey
34+
{
35+
get;
36+
set;
37+
}
38+
39+
DateTimeOffset ITableEntity.Timestamp
40+
{
41+
get;
42+
set;
43+
}
2044

21-
string ITableEntity.ETag
45+
public long LogicalIndex
46+
{
47+
get
2248
{
23-
get;
24-
set;
49+
return AzureEntityList<ErrorEntity>.GetLogicalIndex(this);
2550
}
51+
}
52+
53+
public ErrorEntity() { }
54+
55+
public ErrorEntity(Error error)
56+
{
57+
Error = error;
58+
}
59+
60+
void ITableEntity.ReadEntity(IDictionary<string, EntityProperty> properties, Microsoft.WindowsAzure.Storage.OperationContext operationContext)
61+
{
62+
// This can occasionally fail because someone didn't finish creating the entity yet.
2663

27-
string ITableEntity.PartitionKey
64+
EntityProperty value;
65+
if (properties.TryGetValue("SerializedError", out value))
2866
{
29-
get;
30-
set;
67+
Error = ErrorXml.DecodeString(value.StringValue);
3168
}
32-
33-
string ITableEntity.RowKey
69+
else
3470
{
35-
get;
36-
set;
71+
Error = new Error
72+
{
73+
ApplicationName = "TableErrorLog",
74+
StatusCode = 999,
75+
HostName = Environment.MachineName,
76+
Time = DateTime.UtcNow,
77+
Type = typeof(Exception).FullName,
78+
Detail = "Error Log Entry is Corrupted/Missing in Table Store"
79+
};
80+
81+
return;
3782
}
3883

39-
DateTimeOffset ITableEntity.Timestamp
84+
if (properties.TryGetValue("Detail", out value))
4085
{
41-
get;
42-
set;
86+
Error.Detail = value.StringValue;
4387
}
4488

45-
public long LogicalIndex
89+
if (properties.TryGetValue("WebHostHtmlMessage", out value))
4690
{
47-
get
48-
{
49-
return AzureEntityList<ErrorEntity>.GetLogicalIndex(this);
50-
}
91+
Error.WebHostHtmlMessage = value.StringValue;
5192
}
93+
}
5294

53-
public ErrorEntity() { }
95+
IDictionary<string, EntityProperty> ITableEntity.WriteEntity(OperationContext operationContext)
96+
{
97+
// Table storage has a limitation on property lengths - 64KiB.
98+
// Strings will be encoded as UTF-16, apparently?
99+
100+
const int MaxChars = 32 * 1000;
54101

55-
public ErrorEntity(Error error)
102+
var detail = Error.Detail;
103+
if (detail.Length > MaxChars)
56104
{
57-
Error = error;
105+
detail = detail.Substring(0, MaxChars);
58106
}
59107

60-
void ITableEntity.ReadEntity(IDictionary<string, EntityProperty> properties, Microsoft.WindowsAzure.Storage.OperationContext operationContext)
108+
var htmlMessage = Error.WebHostHtmlMessage;
109+
if (htmlMessage.Length > MaxChars)
61110
{
62-
// This can occasionally fail because someone didn't finish creating the entity yet.
111+
htmlMessage = htmlMessage.Substring(0, MaxChars);
112+
}
63113

64-
EntityProperty value;
65-
if (properties.TryGetValue("SerializedError", out value))
66-
{
67-
Error = ErrorXml.DecodeString(value.StringValue);
68-
}
69-
else
70-
{
71-
Error = new Error
114+
Error.Detail = null;
115+
Error.WebHostHtmlMessage = null;
116+
string serializedError = ErrorXml.EncodeString(Error);
117+
118+
if (serializedError.Length > MaxChars)
119+
{
120+
serializedError = ErrorXml.EncodeString(
121+
new Error
72122
{
73123
ApplicationName = "TableErrorLog",
74-
StatusCode = 999,
124+
StatusCode = 888,
75125
HostName = Environment.MachineName,
76126
Time = DateTime.UtcNow,
77-
Type = typeof(Exception).FullName,
78-
Detail = "Error Log Entry is Corrupted/Missing in Table Store"
79-
};
80-
81-
return;
82-
}
83-
84-
if (properties.TryGetValue("Detail", out value))
85-
{
86-
Error.Detail = value.StringValue;
87-
}
88-
89-
if (properties.TryGetValue("WebHostHtmlMessage", out value))
90-
{
91-
Error.WebHostHtmlMessage = value.StringValue;
92-
}
127+
Detail = "Error Log Entry Will Not Fit In Table Store: " + serializedError.Substring(0, 4000)
128+
});
93129
}
94130

95-
IDictionary<string, EntityProperty> ITableEntity.WriteEntity(OperationContext operationContext)
96-
{
97-
// Table storage has a limitation on property lengths - 64KiB.
98-
// Strings will be encoded as UTF-16, apparently?
99-
100-
const int MaxChars = 32 * 1000;
101-
102-
var detail = Error.Detail;
103-
if (detail.Length > MaxChars)
104-
{
105-
detail = detail.Substring(0, MaxChars);
106-
}
107-
108-
var htmlMessage = Error.WebHostHtmlMessage;
109-
if (htmlMessage.Length > MaxChars)
110-
{
111-
htmlMessage = htmlMessage.Substring(0, MaxChars);
112-
}
113-
114-
Error.Detail = null;
115-
Error.WebHostHtmlMessage = null;
116-
string serializedError = ErrorXml.EncodeString(Error);
117-
118-
if (serializedError.Length > MaxChars)
119-
{
120-
serializedError = ErrorXml.EncodeString(
121-
new Error
122-
{
123-
ApplicationName = "TableErrorLog",
124-
StatusCode = 888,
125-
HostName = Environment.MachineName,
126-
Time = DateTime.UtcNow,
127-
Detail = "Error Log Entry Will Not Fit In Table Store: " + serializedError.Substring(0, 4000)
128-
});
129-
}
130-
131-
return new Dictionary<string, EntityProperty>
131+
return new Dictionary<string, EntityProperty>
132132
{
133133
{ "SerializedError", EntityProperty.GeneratePropertyForString(serializedError) },
134134
{ "Detail", EntityProperty.GeneratePropertyForString(detail) },
135135
{ "WebHostHtmlMessage", EntityProperty.GeneratePropertyForString(htmlMessage) },
136136
};
137-
}
138137
}
138+
}
139139

140-
public class TableErrorLog : ErrorLog
140+
public class TableErrorLog : ErrorLog
141+
{
142+
public const string TableName = "ElmahErrors";
143+
144+
private readonly string _connectionString;
145+
private readonly AzureEntityList<ErrorEntity> _entityList;
146+
147+
public TableErrorLog(string connectionString, bool readAccessGeoRedundant)
141148
{
142-
public const string TableName = "ElmahErrors";
149+
_connectionString = connectionString;
150+
_entityList = new AzureEntityList<ErrorEntity>(connectionString, TableName, readAccessGeoRedundant);
151+
}
143152

144-
private readonly string _connectionString;
145-
private readonly AzureEntityList<ErrorEntity> _entityList;
153+
public override ErrorLogEntry GetError(string id)
154+
{
155+
long pos = Int64.Parse(id, CultureInfo.InvariantCulture);
156+
var error = _entityList[pos];
157+
Debug.Assert(id == pos.ToString(CultureInfo.InvariantCulture));
158+
return new ErrorLogEntry(this, id, error.Error);
159+
}
146160

147-
public TableErrorLog(string connectionString, bool readAccessGeoRedundant)
161+
public override int GetErrors(int pageIndex, int pageSize, IList errorEntryList)
162+
{
163+
// A little math is required since the AzureEntityList is in ascending order
164+
// And we want to retrieve entries in descending order
165+
long queryOffset = _entityList.LongCount - ((pageIndex + 1) * pageSize);
166+
if (queryOffset < 0)
148167
{
149-
_connectionString = connectionString;
150-
_entityList = new AzureEntityList<ErrorEntity>(connectionString, TableName, readAccessGeoRedundant);
168+
pageSize += (int)queryOffset;
169+
queryOffset = 0;
151170
}
152171

153-
public override ErrorLogEntry GetError(string id)
172+
// And since that range was in ascending, flip it to descending.
173+
var results = _entityList.GetRange(queryOffset, pageSize).Reverse();
174+
foreach (var error in results)
154175
{
155-
long pos = Int64.Parse(id, CultureInfo.InvariantCulture);
156-
var error = _entityList[pos];
157-
Debug.Assert(id == pos.ToString(CultureInfo.InvariantCulture));
158-
return new ErrorLogEntry(this, id, error.Error);
176+
string id = error.LogicalIndex.ToString(CultureInfo.InvariantCulture);
177+
errorEntryList.Add(new ErrorLogEntry(this, id, error.Error));
159178
}
160179

161-
public override int GetErrors(int pageIndex, int pageSize, IList errorEntryList)
162-
{
163-
// A little math is required since the AzureEntityList is in ascending order
164-
// And we want to retrieve entries in descending order
165-
long queryOffset = _entityList.LongCount - ((pageIndex+1) * pageSize);
166-
if (queryOffset < 0)
167-
{
168-
pageSize += (int)queryOffset;
169-
queryOffset = 0;
170-
}
171-
172-
// And since that range was in ascending, flip it to descending.
173-
var results = _entityList.GetRange(queryOffset, pageSize).Reverse();
174-
foreach (var error in results)
175-
{
176-
string id = error.LogicalIndex.ToString(CultureInfo.InvariantCulture);
177-
errorEntryList.Add(new ErrorLogEntry(this, id, error.Error));
178-
}
180+
return _entityList.Count;
181+
}
179182

180-
return _entityList.Count;
181-
}
183+
public override string Log(Error error)
184+
{
185+
Obfuscate(error);
186+
var entity = new ErrorEntity(error);
187+
long pos = _entityList.Add(entity);
188+
return pos.ToString(CultureInfo.InvariantCulture);
189+
}
182190

183-
public override string Log(Error error)
191+
private void Obfuscate(Error error)
192+
{
193+
error.User = string.Empty;
194+
if (error.Form != null)
184195
{
185-
Obfuscate(error);
186-
var entity = new ErrorEntity(error);
187-
long pos = _entityList.Add(entity);
188-
return pos.ToString(CultureInfo.InvariantCulture);
196+
error.Form.Clear();
189197
}
190198

191-
private void Obfuscate(Error error)
199+
//ServerVariables overrides requiring context from the http request should be handled in NuGetGallery.QuietLog
200+
var elmahException = error.Exception as ElmahException;
201+
if (elmahException != null)
192202
{
193-
error.User = string.Empty;
194-
if (error.Form != null)
203+
var piiServerVaribles = elmahException.ServerVariables;
204+
foreach (var key in piiServerVaribles.Keys)
195205
{
196-
error.Form.Clear();
206+
error.ServerVariables[key] = piiServerVaribles[key];
197207
}
208+
}
198209

199-
//ServerVariables overrides requiring context from the http request should be handled in NuGetGallery.QuietLog
200-
var elmahException = error.Exception as ElmahException;
201-
if (elmahException != null)
202-
{
203-
var piiServerVaribles = elmahException.ServerVariables;
204-
foreach (var key in piiServerVaribles.Keys)
205-
{
206-
error.ServerVariables[key] = piiServerVaribles[key];
207-
}
208-
}
210+
error.ServerVariables["ALL_HTTP"] = string.Empty;
211+
error.ServerVariables["ALL_RAW"] = string.Empty;
209212

210-
error.ServerVariables["AUTH_USER"] = string.Empty;
211-
error.ServerVariables["LOGON_USER"] = string.Empty;
212-
error.ServerVariables["REMOTE_USER"] = string.Empty;
213+
error.ServerVariables["AUTH_USER"] = string.Empty;
214+
error.ServerVariables["LOGON_USER"] = string.Empty;
215+
error.ServerVariables["REMOTE_USER"] = string.Empty;
213216

214-
error.ServerVariables["REMOTE_ADDR"] = Obfuscator.ObfuscateIp(error.ServerVariables["REMOTE_ADDR"]);
215-
error.ServerVariables["REMOTE_HOST"] = Obfuscator.ObfuscateIp(error.ServerVariables["REMOTE_HOST"]);
216-
error.ServerVariables["LOCAL_ADDR"] = Obfuscator.ObfuscateIp(error.ServerVariables["LOCAL_ADDR"]);
217-
}
217+
error.ServerVariables["REMOTE_ADDR"] = Obfuscator.ObfuscateIp(error.ServerVariables["REMOTE_ADDR"]);
218+
error.ServerVariables["REMOTE_HOST"] = Obfuscator.ObfuscateIp(error.ServerVariables["REMOTE_HOST"]);
219+
error.ServerVariables["LOCAL_ADDR"] = Obfuscator.ObfuscateIp(error.ServerVariables["LOCAL_ADDR"]);
220+
221+
error.ServerVariables["HTTP_X_NUGET_APIKEY"] = string.Empty;
222+
223+
var forwardedIps = error.ServerVariables["HTTP_X_FORWARDED_FOR"].Split(',');
224+
var obfuscatedIps = forwardedIps.Select(Obfuscator.ObfuscateIp);
225+
226+
error.ServerVariables["HTTP_X_FORWARDED_FOR"] = string.Join(",", obfuscatedIps);
218227
}
228+
}
219229
}

0 commit comments

Comments
 (0)