Skip to content

Commit de0855c

Browse files
committed
Merge pull request #179 from exceptionless/feature/clientip
#178 Added the ability to pass the clients submission ip address into the events request info
2 parents b8f59ba + b4b8f8a commit de0855c

File tree

7 files changed

+76
-41
lines changed

7 files changed

+76
-41
lines changed

Source/Core/Extensions/PersistentEventExtensions.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -221,18 +221,19 @@ public static IEnumerable<string> GetIpAddresses(this PersistentEvent ev) {
221221
yield break;
222222

223223
if (!String.IsNullOrEmpty(ev.Geo) && (ev.Geo.Contains(".") || ev.Geo.Contains(":")))
224-
yield return ev.Geo;
224+
yield return ev.Geo.Trim();
225225

226-
var request = ev.GetRequestInfo();
227-
if (!String.IsNullOrEmpty(request?.ClientIpAddress))
228-
yield return request.ClientIpAddress;
229-
230-
var environmentInfo = ev.GetEnvironmentInfo();
231-
if (String.IsNullOrEmpty(environmentInfo?.IpAddress))
232-
yield break;
226+
var ri = ev.GetRequestInfo();
227+
if (!String.IsNullOrEmpty(ri?.ClientIpAddress)) {
228+
foreach (var ip in ri.ClientIpAddress.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
229+
yield return ip.Trim();
230+
}
233231

234-
foreach (var ip in environmentInfo.IpAddress.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
235-
yield return ip;
232+
var ei = ev.GetEnvironmentInfo();
233+
if (!String.IsNullOrEmpty(ei?.IpAddress)) {
234+
foreach (var ip in ei.IpAddress.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
235+
yield return ip.Trim();
236+
}
236237
}
237238

238239
private static bool IsValidIdentifier(string value) {

Source/Core/Extensions/StringExtensions.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,18 @@
88

99
namespace Exceptionless.Core.Extensions {
1010
public static class StringExtensions {
11+
public static bool IsLocalHost(this string ip) {
12+
if (String.IsNullOrEmpty(ip))
13+
return false;
14+
15+
return String.Equals(ip, "::1") || String.Equals(ip, "127.0.0.1");
16+
}
17+
1118
public static bool IsPrivateNetwork(this string ip) {
1219
if (String.IsNullOrEmpty(ip))
1320
return false;
1421

15-
if (String.Equals(ip, "::1") || String.Equals(ip, "127.0.0.1"))
22+
if (ip.IsLocalHost())
1623
return true;
1724

1825
// 10.0.0.0 – 10.255.255.255 (Class A)

Source/Core/Jobs/EventPostsJob.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ protected override async Task<JobResult> ProcessQueueEntryAsync(JobQueueEntryCon
102102
var created = DateTime.UtcNow;
103103
try {
104104
events.ForEach(e => e.CreatedUtc = created);
105-
var results = await _eventPipeline.RunAsync(events.Take(eventsToProcess).ToList()).AnyContext();
105+
var results = await _eventPipeline.RunAsync(events.Take(eventsToProcess).ToList(), eventPostInfo).AnyContext();
106106
Logger.Info().Message("Ran {0} events through the pipeline: id={1} project={2} success={3} error={4}", results.Count, queueEntry.Id, eventPostInfo.ProjectId, results.Count(r => r.IsProcessed), results.Count(r => r.HasError)).WriteIf(!isInternalProject);
107107
foreach (var eventContext in results) {
108108
if (eventContext.IsCancelled)

Source/Core/Pipeline/EventPipeline.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Exceptionless.Core.Repositories;
1010
using Exceptionless.Core.Models;
1111
using Exceptionless.Core.Helpers;
12+
using Exceptionless.Core.Queues.Models;
1213
using Exceptionless.Core.Repositories.Base;
1314
using Foundatio.Metrics;
1415

@@ -24,12 +25,12 @@ public EventPipeline(IDependencyResolver dependencyResolver, IOrganizationReposi
2425
_metricsClient = metricsClient;
2526
}
2627

27-
public Task<EventContext> RunAsync(PersistentEvent ev) {
28-
return RunAsync(new EventContext(ev));
28+
public Task<EventContext> RunAsync(PersistentEvent ev, EventPostInfo epi = null) {
29+
return RunAsync(new EventContext(ev, epi));
2930
}
3031

31-
public Task<ICollection<EventContext>> RunAsync(IEnumerable<PersistentEvent> events) {
32-
return RunAsync(events.Select(ev => new EventContext(ev)).ToList());
32+
public Task<ICollection<EventContext>> RunAsync(IEnumerable<PersistentEvent> events, EventPostInfo epi = null) {
33+
return RunAsync(events.Select(ev => new EventContext(ev, epi)).ToList());
3334
}
3435

3536
public override async Task<ICollection<EventContext>> RunAsync(ICollection<EventContext> contexts) {

Source/Core/Plugins/EventProcessor/Default/40_RequestInfoPlugin.cs

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,57 @@ public override async Task EventBatchProcessingAsync(ICollection<EventContext> c
4040
if (request == null)
4141
continue;
4242

43-
var info = await _parser.ParseAsync(request.UserAgent, context.Project.Id).AnyContext();
44-
if (info != null) {
45-
if (!String.Equals(info.UserAgent.Family, "Other")) {
46-
request.Data[RequestInfo.KnownDataKeys.Browser] = info.UserAgent.Family;
47-
if (!String.IsNullOrEmpty(info.UserAgent.Major)) {
48-
request.Data[RequestInfo.KnownDataKeys.BrowserVersion] = String.Join(".", new[] { info.UserAgent.Major, info.UserAgent.Minor, info.UserAgent.Patch }.Where(v => !String.IsNullOrEmpty(v)));
49-
request.Data[RequestInfo.KnownDataKeys.BrowserMajorVersion] = info.UserAgent.Major;
50-
}
51-
}
43+
AddClientIPAddress(request, context.EventPostInfo?.IpAddress);
44+
await SetBrowserOsAndDeviceFromUserAgent(request, context);
45+
46+
context.Event.AddRequestInfo(request.ApplyDataExclusions(exclusions, MAX_VALUE_LENGTH));
47+
}
48+
}
49+
50+
private void AddClientIPAddress(RequestInfo request, string clientIPAddress) {
51+
if (String.IsNullOrEmpty(clientIPAddress))
52+
return;
53+
54+
if (clientIPAddress.IsLocalHost())
55+
clientIPAddress = "127.0.0.1";
5256

53-
if (!String.Equals(info.Device.Family, "Other"))
54-
request.Data[RequestInfo.KnownDataKeys.Device] = info.Device.Family;
57+
var ips = (request.ClientIpAddress ?? String.Empty)
58+
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
59+
.Select(ip => ip.Trim())
60+
.Where(ip => !ip.IsLocalHost())
61+
.ToList();
5562

63+
if (ips.Count == 0 || !clientIPAddress.IsLocalHost())
64+
ips.Add(clientIPAddress);
5665

57-
if (!String.Equals(info.OS.Family, "Other")) {
58-
request.Data[RequestInfo.KnownDataKeys.OS] = info.OS.Family;
59-
if (!String.IsNullOrEmpty(info.OS.Major)) {
60-
request.Data[RequestInfo.KnownDataKeys.OSVersion] = String.Join(".", new[] { info.OS.Major, info.OS.Minor, info.OS.Patch }.Where(v => !String.IsNullOrEmpty(v)));
61-
request.Data[RequestInfo.KnownDataKeys.OSMajorVersion] = info.OS.Major;
62-
}
66+
request.ClientIpAddress = ips.Distinct().ToDelimitedString();
67+
}
68+
69+
private async Task SetBrowserOsAndDeviceFromUserAgent(RequestInfo request, EventContext context) {
70+
var info = await _parser.ParseAsync(request.UserAgent, context.Project.Id).AnyContext();
71+
if (info != null) {
72+
if (!String.Equals(info.UserAgent.Family, "Other")) {
73+
request.Data[RequestInfo.KnownDataKeys.Browser] = info.UserAgent.Family;
74+
if (!String.IsNullOrEmpty(info.UserAgent.Major)) {
75+
request.Data[RequestInfo.KnownDataKeys.BrowserVersion] = String.Join(".", new[] { info.UserAgent.Major, info.UserAgent.Minor, info.UserAgent.Patch }.Where(v => !String.IsNullOrEmpty(v)));
76+
request.Data[RequestInfo.KnownDataKeys.BrowserMajorVersion] = info.UserAgent.Major;
6377
}
78+
}
6479

65-
var botPatterns = context.Project.Configuration.Settings.ContainsKey(SettingsDictionary.KnownKeys.UserAgentBotPatterns)
66-
? context.Project.Configuration.Settings.GetStringCollection(SettingsDictionary.KnownKeys.UserAgentBotPatterns).ToList()
67-
: new List<string>();
80+
if (!String.Equals(info.Device.Family, "Other"))
81+
request.Data[RequestInfo.KnownDataKeys.Device] = info.Device.Family;
6882

69-
request.Data[RequestInfo.KnownDataKeys.IsBot] = info.Device.IsSpider || request.UserAgent.AnyWildcardMatches(botPatterns);
83+
if (!String.Equals(info.OS.Family, "Other")) {
84+
request.Data[RequestInfo.KnownDataKeys.OS] = info.OS.Family;
85+
if (!String.IsNullOrEmpty(info.OS.Major)) {
86+
request.Data[RequestInfo.KnownDataKeys.OSVersion] = String.Join(".", new[] { info.OS.Major, info.OS.Minor, info.OS.Patch }.Where(v => !String.IsNullOrEmpty(v)));
87+
request.Data[RequestInfo.KnownDataKeys.OSMajorVersion] = info.OS.Major;
88+
}
7089
}
7190

72-
context.Event.AddRequestInfo(request.ApplyDataExclusions(exclusions, MAX_VALUE_LENGTH));
91+
var botPatterns = context.Project.Configuration.Settings.ContainsKey(SettingsDictionary.KnownKeys.UserAgentBotPatterns) ? context.Project.Configuration.Settings.GetStringCollection(SettingsDictionary.KnownKeys.UserAgentBotPatterns).ToList() : new List<string>();
92+
93+
request.Data[RequestInfo.KnownDataKeys.IsBot] = info.Device.IsSpider || request.UserAgent.AnyWildcardMatches(botPatterns);
7394
}
7495
}
7596
}

Source/Core/Plugins/EventProcessor/Default/50_GeoPlugin.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,16 @@ public override async Task EventBatchProcessingAsync(ICollection<EventContext> c
2929

3030
// The geo coordinates are all the same, set the location from the result of any of the ip addresses.
3131
if (!String.IsNullOrEmpty(group.Key)) {
32-
result = await GetGeoFromIPAddressesAsync(group.SelectMany(c => c.Event.GetIpAddresses()).Distinct()).AnyContext();
32+
var ips = group.SelectMany(c => c.Event.GetIpAddresses()).Union(new[] { group.First().EventPostInfo?.IpAddress }).Distinct();
33+
result = await GetGeoFromIPAddressesAsync(ips).AnyContext();
3334
group.ForEach(c => UpdateGeoAndlocation(c.Event, result));
3435
continue;
3536
}
3637

3738
// Each event could be a different user;
3839
foreach (var context in group) {
39-
result = await GetGeoFromIPAddressesAsync(context.Event.GetIpAddresses()).AnyContext();
40+
var ips = context.Event.GetIpAddresses().Union(new[] { context.EventPostInfo?.IpAddress });
41+
result = await GetGeoFromIPAddressesAsync(ips).AnyContext();
4042
UpdateGeoAndlocation(context.Event, result);
4143
}
4244
}

Source/Core/Plugins/EventProcessor/EventContext.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@
33
using Exceptionless.Core.Pipeline;
44
using Exceptionless.Core.Utility;
55
using Exceptionless.Core.Models;
6+
using Exceptionless.Core.Queues.Models;
67

78
namespace Exceptionless.Core.Plugins.EventProcessor {
89
public class EventContext : ExtensibleObject, IPipelineContext {
9-
public EventContext(PersistentEvent ev) {
10+
public EventContext(PersistentEvent ev, EventPostInfo epi = null) {
1011
Event = ev;
12+
EventPostInfo = epi;
1113
StackSignatureData = new Dictionary<string, string>();
1214
}
1315

1416
public PersistentEvent Event { get; set; }
17+
public EventPostInfo EventPostInfo { get; set; }
1518
public Stack Stack { get; set; }
1619
public Project Project { get; set; }
1720
public Organization Organization { get; set; }

0 commit comments

Comments
 (0)