From 3c68221d31845f2b64164ac606b8fae5605eb1f1 Mon Sep 17 00:00:00 2001 From: Applied-Duality Date: Mon, 15 Apr 2013 02:35:04 -0700 Subject: [PATCH] First rough working prototype for LINQ to Loggly No device support yet, only HTTP. Search api not fully mapped. No error checking. No doc comments. --- License.txt | 13 +++ Loggly.sln | 31 ++++++ Loggly/Extensions/Extensions.cs | 17 +++ Loggly/Extensions/JsonContent.cs | 29 +++++ Loggly/Loggly.csproj | 77 +++++++++++++ Loggly/Messages/Messages.cs | 92 ++++++++++++++++ Loggly/Properties/AssemblyInfo.cs | 36 ++++++ Loggly/Retrieval/Client.cs | 74 +++++++++++++ Loggly/Retrieval/QueryEvents/Dated.cs | 125 +++++++++++++++++++++ Loggly/Retrieval/QueryEvents/Events.cs | 53 +++++++++ Loggly/Retrieval/QueryEvents/Filtered.cs | 127 ++++++++++++++++++++++ Loggly/Retrieval/QueryEvents/Ordered.cs | 53 +++++++++ Loggly/Retrieval/QueryEvents/Projected.cs | 60 ++++++++++ Loggly/Retrieval/QueryEvents/Query.cs | 10 ++ Loggly/Retrieval/QueryEvents/Skipped.cs | 49 +++++++++ Loggly/Retrieval/QueryEvents/Taken.cs | 93 ++++++++++++++++ Loggly/Retrieval/QueryInputs/Filtered.cs | 104 ++++++++++++++++++ Loggly/Retrieval/QueryInputs/Inputs.cs | 32 ++++++ Loggly/Retrieval/QueryInputs/Selected.cs | 57 ++++++++++ Loggly/Submission/Client.cs | 29 +++++ Loggly/packages.config | 4 + Playground/App.config | 26 +++++ Playground/Playground.csproj | 75 +++++++++++++ Playground/Program.cs | 80 ++++++++++++++ Playground/Properties/AssemblyInfo.cs | 36 ++++++ Playground/_Program.cs | 48 ++++++++ Playground/packages.config | 4 + 27 files changed, 1434 insertions(+) create mode 100644 License.txt create mode 100644 Loggly.sln create mode 100644 Loggly/Extensions/Extensions.cs create mode 100644 Loggly/Extensions/JsonContent.cs create mode 100644 Loggly/Loggly.csproj create mode 100644 Loggly/Messages/Messages.cs create mode 100644 Loggly/Properties/AssemblyInfo.cs create mode 100644 Loggly/Retrieval/Client.cs create mode 100644 Loggly/Retrieval/QueryEvents/Dated.cs create mode 100644 Loggly/Retrieval/QueryEvents/Events.cs create mode 100644 Loggly/Retrieval/QueryEvents/Filtered.cs create mode 100644 Loggly/Retrieval/QueryEvents/Ordered.cs create mode 100644 Loggly/Retrieval/QueryEvents/Projected.cs create mode 100644 Loggly/Retrieval/QueryEvents/Query.cs create mode 100644 Loggly/Retrieval/QueryEvents/Skipped.cs create mode 100644 Loggly/Retrieval/QueryEvents/Taken.cs create mode 100644 Loggly/Retrieval/QueryInputs/Filtered.cs create mode 100644 Loggly/Retrieval/QueryInputs/Inputs.cs create mode 100644 Loggly/Retrieval/QueryInputs/Selected.cs create mode 100644 Loggly/Submission/Client.cs create mode 100644 Loggly/packages.config create mode 100644 Playground/App.config create mode 100644 Playground/Playground.csproj create mode 100644 Playground/Program.cs create mode 100644 Playground/Properties/AssemblyInfo.cs create mode 100644 Playground/_Program.cs create mode 100644 Playground/packages.config diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..c5bb711 --- /dev/null +++ b/License.txt @@ -0,0 +1,13 @@ +Copyright 2013 Applied Duality, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/Loggly.sln b/Loggly.sln new file mode 100644 index 0000000..98c830f --- /dev/null +++ b/Loggly.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Loggly", "Loggly\Loggly.csproj", "{CD3B0557-018C-4AF2-A71D-51175839A501}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Playground", "Playground\Playground.csproj", "{A6A093E5-61E2-4C0E-8A32-17FD5872DD6D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E9379F8A-0BC8-4978-9A49-49BBA483116A}" + ProjectSection(SolutionItems) = preProject + License.txt = License.txt + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CD3B0557-018C-4AF2-A71D-51175839A501}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD3B0557-018C-4AF2-A71D-51175839A501}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD3B0557-018C-4AF2-A71D-51175839A501}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD3B0557-018C-4AF2-A71D-51175839A501}.Release|Any CPU.Build.0 = Release|Any CPU + {A6A093E5-61E2-4C0E-8A32-17FD5872DD6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6A093E5-61E2-4C0E-8A32-17FD5872DD6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6A093E5-61E2-4C0E-8A32-17FD5872DD6D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6A093E5-61E2-4C0E-8A32-17FD5872DD6D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Loggly/Extensions/Extensions.cs b/Loggly/Extensions/Extensions.cs new file mode 100644 index 0000000..0407f13 --- /dev/null +++ b/Loggly/Extensions/Extensions.cs @@ -0,0 +1,17 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System.Threading.Tasks; + +namespace Loggly +{ + public static class Extensions + { + public static async void ToBackGround(this Task task) + { + try{ await task; } catch {} + } + } +} diff --git a/Loggly/Extensions/JsonContent.cs b/Loggly/Extensions/JsonContent.cs new file mode 100644 index 0000000..e23311c --- /dev/null +++ b/Loggly/Extensions/JsonContent.cs @@ -0,0 +1,29 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System.Net.Http; + +// Great blog post about HttpClient +// http://pfelix.wordpress.com/2012/01/11/the-new-net-httpclient-class/ + +namespace Loggly +{ + /// + /// JsonContent helper class. + /// + public class JsonContent : StringContent + { + public JsonContent() : this(content: "{}") { } + + public JsonContent(System.Json.JsonValue content) : this(content: content.ToString()) { } + + public JsonContent(string content) + : base(content: content) + { + base.Headers.ContentType.CharSet = ""; + base.Headers.ContentType.MediaType = "application/json"; + } + } +} diff --git a/Loggly/Loggly.csproj b/Loggly/Loggly.csproj new file mode 100644 index 0000000..f1406e0 --- /dev/null +++ b/Loggly/Loggly.csproj @@ -0,0 +1,77 @@ + + + + + Debug + AnyCPU + {CD3B0557-018C-4AF2-A71D-51175839A501} + Library + Properties + Loggly + Loggly + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + ..\..\..\..\..\Dropbox\Dlls\System.Json.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Loggly/Messages/Messages.cs b/Loggly/Messages/Messages.cs new file mode 100644 index 0000000..9d609ba --- /dev/null +++ b/Loggly/Messages/Messages.cs @@ -0,0 +1,92 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Collections.Generic; +using System.Json; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; + +namespace Loggly +{ + + public enum EventFormat { Other, Text, Json } + + public struct Service + { + dynamic _json; + public Service(JsonValue json) { this._json = json; } + + public string Name { get { return _json.name; } } + public string Display { get { return _json.display; } } + + public override string ToString() + { + return _json.ToString(); + } + } + + public struct HttpInput + { + dynamic _json; + public HttpInput(JsonValue json) { this._json = json; } + public HttpInput(string json):this(JsonValue.Parse(json)) { } + public string Description { get { return _json.description; } } + public int Id { get { return _json.id; } } + public Guid InputToken { get { return Guid.Parse(_json.input_token); } } + public DateTimeOffset Created { get { return DateTimeOffset.Parse(_json.created); } } + public EventFormat Format + { + get + { + string format = _json.format; + return format == "text" ? EventFormat.Text : format == "json" ? EventFormat.Json : EventFormat.Other; + } + } + public string Name { get { return _json.name; } } + public Uri LoggingUrl + { + get + { + string uri = _json.logging_url; + return new Uri(uri); + } + } + public Service Service { get { return new Service(_json.service); } } + + public override string ToString() + { + return _json.ToString(); + } + } + + public struct _HttpInputs + { + dynamic _json; + internal _HttpInputs(string json) + { + var p = JsonValue.Parse(json); + if (p.JsonType != JsonType.Array) p = new JsonArray(p); + _json = p; + + } + + public IEnumerable Inputs + { + get + { + var xs = _json as IEnumerable; + return (xs).Select(input => new HttpInput(input)); + } + } + + public override string ToString() + { + return _json.ToString(); + } + } +} \ No newline at end of file diff --git a/Loggly/Properties/AssemblyInfo.cs b/Loggly/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7a5bb2e --- /dev/null +++ b/Loggly/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Loggly")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Loggly")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7ed8efe8-0be0-433e-b55a-606368a8ebfe")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Loggly/Retrieval/Client.cs b/Loggly/Retrieval/Client.cs new file mode 100644 index 0000000..d79631c --- /dev/null +++ b/Loggly/Retrieval/Client.cs @@ -0,0 +1,74 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; + +namespace Loggly.Retrieval +{ + public partial class Client + { + HttpClient _client; + public Client(string username, string password, string subdomain) + { + _client = new HttpClient(); + var header = new AuthenticationHeaderValue + ("Basic" + , Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(string.Format("{0}:{1}", username, password))) + ); + _client.DefaultRequestHeaders.Authorization = header; + var baseUri = new Uri(string.Format("https://{0}.loggly.com/api/",subdomain)); + _client.BaseAddress = baseUri; + } + + public Inputs GetInputsAsync() + { + return new Inputs(_client); + } + + public async Task CreateHttpInputAsync(string name, string description = "", EventFormat format = EventFormat.Json) + { + var content = new FormUrlEncodedContent + (new Dictionary + { + {"name" , name}, + {"description", description}, + {"service", "http" }, + { "format", "json" }, + }); + var response = await _client.PostAsync("inputs/", content); + if (response.IsSuccessStatusCode) + { + var json = await response.Content.ReadAsStringAsync(); + return new HttpInput(json); + } + else + { + return null; + } + } + + public async Task DeleteHttpInputAsync(HttpInput input) + { + var response = await _client.DeleteAsync(string.Format("inputs/{0}", input.Id)); + return response.StatusCode == System.Net.HttpStatusCode.NoContent; + } + + public async Task SearchAsync(string query) + { + var response = await _client.GetAsync(string.Format("search?{0}",query)); + return await response.Content.ReadAsStringAsync(); + } + + public Events QueryEventsAsync() + { + return new Loggly.Retrieval.Events(_client); + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryEvents/Dated.cs b/Loggly/Retrieval/QueryEvents/Dated.cs new file mode 100644 index 0000000..9bf726e --- /dev/null +++ b/Loggly/Retrieval/QueryEvents/Dated.cs @@ -0,0 +1,125 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; + +/// For now only absolute time computed on client +namespace Loggly.Retrieval +{ + public struct DatedEvents + { + HttpClient _client; + Expression> _pattern; + Expression> _timeRange; + + public DatedEvents + (HttpClient client + , Expression> pattern + , Expression> timeRange) + { + _client = client; + _pattern = pattern; + _timeRange = timeRange; + } + + public OrderedEvents OrderBy(Expression> keySelector) + { + return new OrderedEvents(_client, _pattern, _timeRange, false); + } + public OrderedEvents OrderByDescending(Expression> keySelector) + { + return new OrderedEvents(_client, _pattern, _timeRange, true); + } + public ProjectedEvents Select(Expression> selector) + { + return new ProjectedEvents(_client, _pattern, _timeRange, true, selector); + } + public SkippedEvents Skip(int n) + { + return new SkippedEvents(_client, _pattern, _timeRange, true, null, 0); + } + public TakenEvents Take(int n) + { + return new TakenEvents(_client, _pattern, _timeRange, true, null, 0, 10); + } + public TaskAwaiter GetAwaiter() + { + return this.Take(10).GetAwaiter(); + } + + public struct Time + { + public static Bool operator <=(DateTimeOffset start, Time time) + { + return new Bool(start: start); + } + public static Bool operator <=(Time time, DateTimeOffset end) + { + return new Bool(end: end); + } + public static Bool operator >=(DateTimeOffset end, Time time) + { + return time <= end; + } + public static Bool operator >=(Time time, DateTimeOffset start) + { + return start <= time; + } + } + public struct Bool + { + public DateTimeOffset _start; + public DateTimeOffset _end; + + public Bool(DateTimeOffset start = default(DateTimeOffset), DateTimeOffset end = default(DateTimeOffset)) + { + _start = start; + _end = end; + } + + public static Bool operator &(Bool left, Bool right) + { + var b = new Bool(left.Start, left.End); + b.Start = right.Start; + b.End = right.End; + return b; + } + public static bool operator true(Bool p) + { + return false; + } + public static bool operator false(Bool p) + { + return false; + } + + DateTimeOffset Start + { + set + { + if (value == default(DateTimeOffset)) return; + if (_start == default(DateTimeOffset) || value < _start) _start = value; + } + get { return _start; } + } + DateTimeOffset End + { + set + { + if (value == default(DateTimeOffset)) return; + if (_end == default(DateTimeOffset) || value > _end) _end = value; + } + get { return _end; } + } + } + public struct Event + { + public Time Time { get { return new Time(); } } + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryEvents/Events.cs b/Loggly/Retrieval/QueryEvents/Events.cs new file mode 100644 index 0000000..0ce7191 --- /dev/null +++ b/Loggly/Retrieval/QueryEvents/Events.cs @@ -0,0 +1,53 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace Loggly.Retrieval +{ + public struct Events + { + HttpClient _client; + public Events(HttpClient client) { _client = client; } + + public FilteredEvents Where(Expression> pattern) + { + return new FilteredEvents(_client, pattern); + } + public DatedEvents Where(Expression> timeRange) + { + return new DatedEvents(_client, null, timeRange); + } + public OrderedEvents OrderBy(Expression> keySelector) + { + return new OrderedEvents(_client, null, null, false); + } + public OrderedEvents OrderByDescending(Expression> keySelector) + { + return new OrderedEvents(_client, null, null, true); + } + public ProjectedEvents Select(Expression> selector) + { + return new ProjectedEvents(_client, null, null, true, selector); + } + public SkippedEvents Skip(int n) + { + return new SkippedEvents(_client, null, null, true, null, 0); + } + public TakenEvents Take(int n) + { + return new TakenEvents(_client, null, null, true, null, 0, 10); + } + + public TaskAwaiter GetAwaiter() + { + return this.Take(10).GetAwaiter(); + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryEvents/Filtered.cs b/Loggly/Retrieval/QueryEvents/Filtered.cs new file mode 100644 index 0000000..2b00279 --- /dev/null +++ b/Loggly/Retrieval/QueryEvents/Filtered.cs @@ -0,0 +1,127 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; + +namespace Loggly.Retrieval +{ + public struct FilteredEvents + { + HttpClient _client; + Expression> _pattern; + + internal FilteredEvents(HttpClient client, Expression> pattern) + { + _client = client; + _pattern = pattern; + } + + public DatedEvents Where(Expression> timeRange) + { + return new DatedEvents(_client, _pattern, timeRange); + } + public OrderedEvents OrderBy(Expression> keySelector) + { + return new OrderedEvents(_client, _pattern, null, false); + } + public OrderedEvents OrderByDescending(Expression> keySelector) + { + return new OrderedEvents(_client, _pattern, null, true); + } + public ProjectedEvents Select(Expression> selector) + { + return new ProjectedEvents(_client, _pattern, null, true, selector); + } + public SkippedEvents Skip(int n) + { + return new SkippedEvents(_client, _pattern, null, true, null, 0); + } + public TakenEvents Take(int n) + { + return new TakenEvents(_client, _pattern, null, true, null, 0, 10); + } + + public TaskAwaiter GetAwaiter() + { + return this.Take(10).GetAwaiter(); + } + + public struct IP + { + public Bool Matches(string pattern) + { + return new Bool(string.Format("ip:{0}", pattern)); + } + } + public struct Pattern + { + public Bool Matches(string pattern) + { + return new Bool(string.Format("{0}", pattern)); + } + } + public struct Name + { + public static Bool operator ==(Name _, string name) + { + return new Bool(string.Format("inputname:{0}", name)); + } + + public static Bool operator ==(string name, Name _) + { + return (_ == name); + } + + [Obsolete] + [EditorBrowsable(EditorBrowsableState.Never)] + public static Bool operator !=(Name _, string name) + { + throw new NotImplementedException(); + } + + [Obsolete] + [EditorBrowsable(EditorBrowsableState.Never)] + public static Bool operator !=(string name, Name _) + { + throw new NotImplementedException(); + } + } + public struct Bool + { + public string _pattern; + public Bool(string pattern) { _pattern = pattern; } + + public static Bool operator &(Bool left, Bool right) + { + return new Bool(string.Format("({0} AND {1})", left._pattern, right._pattern)); + } + public static Bool operator |(Bool left, Bool right) + { + return new Bool(string.Format("({0} OR {1})", left._pattern, right._pattern)); + } + + // http://stackoverflow.com/questions/5203093/how-does-operator-overloading-of-true-and-false-work/5203185#5203185 + // so always return false since we want to compute result eagerly on client + public static bool operator true(Bool p) + { + return false; + } + public static bool operator false(Bool p) + { + return false; + } + } + public struct Event + { + public IP Ip { get { return new IP(); } } + public Pattern Text { get { return new Pattern(); } } + public Name InputName { get { return new Name(); } } + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryEvents/Ordered.cs b/Loggly/Retrieval/QueryEvents/Ordered.cs new file mode 100644 index 0000000..0599246 --- /dev/null +++ b/Loggly/Retrieval/QueryEvents/Ordered.cs @@ -0,0 +1,53 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; + +namespace Loggly.Retrieval +{ + public struct OrderedEvents + { + HttpClient _client; + Expression> _pattern; + Expression> _timeRange; + bool _descending; + + public OrderedEvents + (HttpClient client, Expression> pattern, Expression> timeRange, bool descending) + { + _client = client; + _pattern = pattern; + _timeRange = timeRange; + _descending = descending; + } + + public ProjectedEvents Select(Expression> selector) + { + return new ProjectedEvents(_client, _pattern, _timeRange, _descending, selector); + } + public SkippedEvents Skip(int n) + { + return new SkippedEvents(_client, _pattern, _timeRange, _descending, null, n); + } + public TakenEvents Take(int n) + { + return new TakenEvents(_client, _pattern, _timeRange, _descending, null, 0, n); + } + + public TaskAwaiter GetAwaiter() + { + return this.Take(10).GetAwaiter(); + } + + public struct Time { } + public struct Event + { + public Time Time { get { return new Time(); } } + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryEvents/Projected.cs b/Loggly/Retrieval/QueryEvents/Projected.cs new file mode 100644 index 0000000..39a7049 --- /dev/null +++ b/Loggly/Retrieval/QueryEvents/Projected.cs @@ -0,0 +1,60 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; + +namespace Loggly.Retrieval +{ + public struct ProjectedEvents + { + HttpClient _client; + Expression> _pattern; + Expression> _timeRange; + bool _descending; + Expression> _selector; + + public ProjectedEvents + (HttpClient client + , Expression> pattern + , Expression> timeRange + , bool descending + , Expression> selector + ) + { + _client = client; + _pattern = pattern; + _timeRange = timeRange; + _descending = descending; + _selector = selector; + } + + public SkippedEvents Skip(int n) + { + return new SkippedEvents(_client, _pattern, _timeRange, _descending, _selector, n); + } + public TakenEvents Take(int n) + { + return new TakenEvents(_client, _pattern, _timeRange, _descending, _selector, 0, n); + } + + public TaskAwaiter GetAwaiter() + { + return this.Take(10).GetAwaiter(); + } + + // id', 'timestamp', 'ip', 'inputname', 'text'. + public struct Event + { + public int Id; + public DateTimeOffset TimeStamp; + public string Ip; + public string Text; + public string InputName; + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryEvents/Query.cs b/Loggly/Retrieval/QueryEvents/Query.cs new file mode 100644 index 0000000..8757a9e --- /dev/null +++ b/Loggly/Retrieval/QueryEvents/Query.cs @@ -0,0 +1,10 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + + +namespace Loggly.Retrieval +{ + +} diff --git a/Loggly/Retrieval/QueryEvents/Skipped.cs b/Loggly/Retrieval/QueryEvents/Skipped.cs new file mode 100644 index 0000000..c0d2454 --- /dev/null +++ b/Loggly/Retrieval/QueryEvents/Skipped.cs @@ -0,0 +1,49 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; + +namespace Loggly.Retrieval +{ + public struct SkippedEvents + { + HttpClient _client; + Expression> _pattern; + Expression> _timeRange; + bool _descending; + Expression> _selector; + int _skip; + + public SkippedEvents + (HttpClient client + , Expression> pattern + , Expression> timeRange + , bool descending + , Expression> selector + , int skip + ) + { + _client = client; + _pattern = pattern; + _timeRange = timeRange; + _descending = descending; + _selector = selector; + _skip = skip; + } + + public TakenEvents Take(int n) + { + return new TakenEvents(_client, _pattern, _timeRange, _descending, _selector, _skip, n); + } + + public TaskAwaiter GetAwaiter() + { + return this.Take(10).GetAwaiter(); + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryEvents/Taken.cs b/Loggly/Retrieval/QueryEvents/Taken.cs new file mode 100644 index 0000000..5650218 --- /dev/null +++ b/Loggly/Retrieval/QueryEvents/Taken.cs @@ -0,0 +1,93 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace Loggly.Retrieval +{ + public struct TakenEvents + { + HttpClient _client; + Expression> _pattern; + Expression> _timeRange; + bool _descending; + Expression> _selector; + int _skip; + int _take; + + public TakenEvents + (HttpClient client, Expression> pattern, Expression> timeRange, bool descending, Expression> selector, int skip, int take) + { + _client = client; + _pattern = pattern; + _timeRange = timeRange; + _descending = descending; + _selector = selector; + _skip = skip; + _take = take; + } + + async Task _GetEventsAsync() + { + var query = new Dictionary(); + + if (_pattern != null) query["q"] = _pattern.Compile()(default(FilteredEvents.Event))._pattern; + else query["q"] = "*"; + + if (_timeRange != null) + { + var t = _timeRange.Compile()(default(DatedEvents.Event)); + if(t._start != default(DateTimeOffset)) + query["from"] = t._start.ToString("yyyy-MM-dd HH:mm:ss.fffzzzz"); + if(t._end != default(DateTimeOffset)) + query["until"] = t._end.ToString("yyyy-MM-dd HH:mm:ss.fffzzzz"); + } + + if (!_descending) query["order"] = "asc"; + + if (_selector != null) + { + try + { + var fields = string.Join(",", + (_selector.Body as MemberInitExpression) + .Bindings + .Select(mb => mb.Member.Name.ToLowerInvariant())); + query["fields"] = fields; + } + catch { } + } + + if(_skip > 0) query["start"] = _skip.ToString(); + + if (_take > 0 && _take != 10) query["rows"] = _take.ToString(); + + var queryString = string.Join("&", query.Select(kv => string.Format("{0}={1}", kv.Key, kv.Value))); + + var response = await _client.GetAsync(string.Format("search?{0}", queryString)); + + if (response.IsSuccessStatusCode) + { + return await response.Content.ReadAsStringAsync(); + } + else + { + return ""; + } + } + + // TODO: parse result + public TaskAwaiter GetAwaiter() + { + return _GetEventsAsync().GetAwaiter(); + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryInputs/Filtered.cs b/Loggly/Retrieval/QueryInputs/Filtered.cs new file mode 100644 index 0000000..fe371e8 --- /dev/null +++ b/Loggly/Retrieval/QueryInputs/Filtered.cs @@ -0,0 +1,104 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; + +namespace Loggly.Retrieval +{ + public class FilteredInputs + { + HttpClient _client; + public Expression> _predicate; + + internal FilteredInputs(HttpClient client, Expression> predicate) + { + _client = client; + _predicate = predicate; + } + + public SelectedInputs Select(Expression> selector) + { + return new SelectedInputs(_client, _predicate); + } + + public TaskAwaiter GetAwaiter() + { + return this.Select(x => x).GetAwaiter(); + } + + public struct Id + { + public static Bool operator ==(Id _, int id) + { + return new Bool(id: id); + } + + public static Bool operator ==(int id, Id _) + { + return (_ == id); + } + + [Obsolete] + [EditorBrowsable(EditorBrowsableState.Never)] + public static Bool operator !=(Id _, int id) + { + throw new NotImplementedException(); + } + + [Obsolete] + [EditorBrowsable(EditorBrowsableState.Never)] + public static Bool operator !=(int id, Id _) + { + throw new NotImplementedException(); + } + } + public struct Name + { + public static Bool operator ==(Name _, string name) + { + return new Bool(name: name); + } + + public static Bool operator ==(string name, Name _) + { + return (_ == name); + } + + [Obsolete] + [EditorBrowsable(EditorBrowsableState.Never)] + public static Bool operator !=(Name _, string name) + { + throw new NotImplementedException(); + } + + [Obsolete] + [EditorBrowsable(EditorBrowsableState.Never)] + public static Bool operator !=(string name, Name _) + { + throw new NotImplementedException(); + } + } + public struct Bool + { + internal string _name; + internal int _id; + + public Bool(string name = null, int id = 0) + { + _name = name; + _id = id; + } + } + public struct Input + { + public Id Id { get { return new Id(); } } + public Name Name { get { return new Name(); } } + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryInputs/Inputs.cs b/Loggly/Retrieval/QueryInputs/Inputs.cs new file mode 100644 index 0000000..edc08c1 --- /dev/null +++ b/Loggly/Retrieval/QueryInputs/Inputs.cs @@ -0,0 +1,32 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; + +namespace Loggly.Retrieval +{ + public class Inputs + { + HttpClient _client; + internal Inputs(HttpClient client) { _client = client; } + + public FilteredInputs Where(Expression> predicate) + { + return new FilteredInputs(_client, predicate); + } + public SelectedInputs Select(Expression> selector) + { + return new SelectedInputs(_client, null); + } + + public TaskAwaiter GetAwaiter() + { + return this.Select(x => x).GetAwaiter(); + } + } +} \ No newline at end of file diff --git a/Loggly/Retrieval/QueryInputs/Selected.cs b/Loggly/Retrieval/QueryInputs/Selected.cs new file mode 100644 index 0000000..b3c5b3a --- /dev/null +++ b/Loggly/Retrieval/QueryInputs/Selected.cs @@ -0,0 +1,57 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace Loggly.Retrieval +{ + public class SelectedInputs + { + HttpClient _client; + Expression> _predicate; + + internal SelectedInputs(HttpClient client, Expression> predicate) + { + _client = client; + _predicate = predicate; + } + + async Task _GetInputsAsync() + { + var query = ""; + + if (_predicate != null) + { + var b = _predicate.Compile()(default(FilteredInputs.Input)); + if (!string.IsNullOrWhiteSpace(b._name)) query = string.Format("?name={0}", b._name); + else if (b._id != 0) query = string.Format("{0}", b._id); + } + + var response = await _client.GetAsync(string.Format("inputs/{0}",query)); + + if (response.IsSuccessStatusCode) + { + var json = await response.Content.ReadAsStringAsync(); + return new _HttpInputs(json).Inputs.ToArray(); + } + else + { + return new HttpInput[] { }; + } + } + + public TaskAwaiter GetAwaiter() + { + return _GetInputsAsync().GetAwaiter(); + } + + public struct Input { } + } +} diff --git a/Loggly/Submission/Client.cs b/Loggly/Submission/Client.cs new file mode 100644 index 0000000..4f17009 --- /dev/null +++ b/Loggly/Submission/Client.cs @@ -0,0 +1,29 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System.Json; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Loggly.Submission +{ + public class Client + { + HttpClient _client; + public Client() + { + _client = new HttpClient(); + } + + /// + /// + public async Task PostMessageAsync(HttpInput input, JsonValue json) + { + var content = new JsonContent(json); + var response = await _client.PostAsync(input.LoggingUrl, content); + return response.IsSuccessStatusCode; + } + } +} \ No newline at end of file diff --git a/Loggly/packages.config b/Loggly/packages.config new file mode 100644 index 0000000..d8ffcb7 --- /dev/null +++ b/Loggly/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Playground/App.config b/Playground/App.config new file mode 100644 index 0000000..eb5f3a2 --- /dev/null +++ b/Playground/App.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + Rsa Key + + + WBLZKR1VtSHVB2PwpLV73MGWe4NnbDpvjipcPz1cgtjUPc6ZGOUiY7yBvobSYOK/81rZ7QCKNXbdfwwbkPL01RgFRB6roF4SvLO7M4UFrSY24IGqJyCBGH/Hh0GyWt5bzwQP0qjJn2d5WSq4gfn+R9xgaT5lNEsdrPbn37sJdPE= + + + + + Gq+Sm4Pc04oOKOyvMHSvdbMMV94J645+LkJlNj7sNI52lBBI8PzZ1UgHO9YHWM5I/DSLqGztCllmZ8i5RCE8uVyLlk4MaztlzkcPaNK8rfpDLz9kwIWLmwTwFg5yOLEaQ9hROxI0MK+aEiPebqOod1oLW5zgjK3Nwn4eURD5FxBv1l/Lil4hu1jZnc+2v/sqde00QbsE1mS713qt08tGzuIYk15MOUWei5lHoRA/6SwQ8l8EvtgmAvT3AiG4SsUqT1/6oW1VD/ayzkjaiyGRPQ== + + + + \ No newline at end of file diff --git a/Playground/Playground.csproj b/Playground/Playground.csproj new file mode 100644 index 0000000..8f222fe --- /dev/null +++ b/Playground/Playground.csproj @@ -0,0 +1,75 @@ + + + + + Debug + AnyCPU + {A6A093E5-61E2-4C0E-8A32-17FD5872DD6D} + Exe + Properties + Playground + Playground + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + False + ..\..\..\..\..\Dropbox\Dlls\System.Json.dll + + + + + + + + + + + + + + + + + Designer + + + + + + {cd3b0557-018c-4af2-a71d-51175839a501} + Loggly + + + + + \ No newline at end of file diff --git a/Playground/Program.cs b/Playground/Program.cs new file mode 100644 index 0000000..b993d09 --- /dev/null +++ b/Playground/Program.cs @@ -0,0 +1,80 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using System; +using System.Configuration; +using System.Json; +using System.Threading.Tasks; +using Retrieval = Loggly.Retrieval; +using Submission = Loggly.Submission; + +namespace Playground +{ + partial class Program + { + static void Main(string[] args) + { + //ProtectConfiguration(); + UnProtectConfiguration(); + MainAsync().Wait(); + } + + static async Task MainAsync() + { + // Submission client does not need to authenticate + // gets the Url from the HttpInput + var s = new Submission.Client(); + + // Retrieval and management need to log in to subdomain + var r = new Retrieval.Client + ( ConfigurationManager.AppSettings["username"] + , ConfigurationManager.AppSettings["password"] + , ConfigurationManager.AppSettings["subdomain"] + ); + + // Create new tmp input + // Might change/add API to GetOrCreate like IronMQ + var tmp = await r.CreateHttpInputAsync("tmp"); + + // Look up the newly created input + var inputs = await from i in r.GetInputsAsync() + where i.Name == tmp.Value.Name + select i; + + if (inputs.Length != 0) + { + Console.Write("Send a few messages ... "); + var m = int.Parse(Console.ReadLine()); + for (var n = 0; n < m; n++) + { + // submitting events can take several seconds (> 10) + // use .ToBackGround() to fire and forget + // or put tasks in a list and call .WhenAll to await all + // Also, despite returning 200 OK, some events seem to dissapear + // (or may show up much later, I don't know) + var b = await s.PostMessageAsync + (inputs[0], new JsonObject { { "alert", string.Format("Oops I did it again {0}", n) } }); + + Console.WriteLine("{0}-->{1}", n, b); + } + + // Check if the values were received + // NOTE: I do not fully understand the e.Text.Matches(...) part + // documentation for that on http://loggly.com/support/using-data/search-guide/ is skimpy + var q = await from e in r.QueryEventsAsync() + where e.InputName == inputs[0].Name + select e; + + // Show us what you got ... + Console.WriteLine(q); + } + + // Clean up the queue + var d = await r.DeleteHttpInputAsync(tmp.Value); + + Console.ReadLine(); + } + } +} diff --git a/Playground/Properties/AssemblyInfo.cs b/Playground/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0719551 --- /dev/null +++ b/Playground/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Playground")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Playground")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("cc719ed5-f3c6-4bde-9c7f-5e7a0372992e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Playground/_Program.cs b/Playground/_Program.cs new file mode 100644 index 0000000..d5a5d5c --- /dev/null +++ b/Playground/_Program.cs @@ -0,0 +1,48 @@ +#region Apache 2 License +// Copyright (c) Applied Duality, Inc., All rights reserved. +// See License.txt in the project root for license information. +#endregion + +using Loggly; +using System.Configuration; +using System.Threading.Tasks; + +namespace Playground +{ + partial class Program + { + //http://www.a2zmenu.com/Blogs/CSharp/How-to-encrypt-configuration-file.aspx + //http://msdn.microsoft.com/en-us/library/system.configuration.protectedconfigurationprovider.aspx + + private static void ProtectConfiguration() + { + var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + var provider = "RsaProtectedConfigurationProvider"; + var appSettings = config.AppSettings; + + if (appSettings != null + && !appSettings.SectionInformation.IsProtected + && !appSettings.ElementInformation.IsLocked) + { + appSettings.SectionInformation.ProtectSection(provider); + appSettings.SectionInformation.ForceSave = true; + config.Save(); + } + } + + private static void UnProtectConfiguration() + { + var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + var appSettings = config.AppSettings; + + if (appSettings != null + && appSettings.SectionInformation.IsProtected + && !appSettings.ElementInformation.IsLocked) + { + appSettings.SectionInformation.UnprotectSection(); + appSettings.SectionInformation.ForceSave = true; + config.Save(ConfigurationSaveMode.Full); + } + } + } +} \ No newline at end of file diff --git a/Playground/packages.config b/Playground/packages.config new file mode 100644 index 0000000..d8ffcb7 --- /dev/null +++ b/Playground/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file