Skip to content

Commit aab5ddf

Browse files
authored
Merge pull request #1 from develop
2 parents 5cb2bd3 + 390dedb commit aab5ddf

22 files changed

+940
-0
lines changed

.editorconfig

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
2+
###############################
3+
# Core EditorConfig Options #
4+
###############################
5+
# All files
6+
[*]
7+
indent_style = space
8+
# Code files
9+
[*.{cs,csx}]
10+
indent_size = 4
11+
insert_final_newline = true
12+
charset = utf-8-bom
13+
###############################
14+
# .NET Coding Conventions #
15+
###############################
16+
[*.{cs,vb}]
17+
# Organize usings
18+
dotnet_sort_system_directives_first = true
19+
# this. preferences
20+
dotnet_style_qualification_for_field = true:warning
21+
dotnet_style_qualification_for_property = false:warning
22+
dotnet_style_qualification_for_method = false:warning
23+
dotnet_style_qualification_for_event = false:warning
24+
# Language keywords vs BCL types preferences
25+
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
26+
dotnet_style_predefined_type_for_member_access = false:suggestion
27+
# Parentheses preferences
28+
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion
29+
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion
30+
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:suggestion
31+
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion
32+
# Modifier preferences
33+
dotnet_style_require_accessibility_modifiers = always:warning
34+
dotnet_style_readonly_field = true:suggestion
35+
# Expression-level preferences
36+
dotnet_style_object_initializer = true:warning
37+
dotnet_style_collection_initializer = true:warning
38+
dotnet_style_explicit_tuple_names = true:suggestion
39+
dotnet_style_null_propagation = true:warning
40+
dotnet_style_coalesce_expression = true:warning
41+
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error
42+
dotnet_style_explicit_tuple_names = true:error
43+
dotnet_prefer_inferred_tuple_names = true:suggestion
44+
dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion
45+
dotnet_style_prefer_auto_properties = true:warning
46+
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
47+
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
48+
###############################
49+
# Naming Conventions #
50+
###############################
51+
# Style Definitions
52+
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
53+
dotnet_naming_style.camel_case_style.capitalization = camel_case
54+
dotnet_naming_style.end_in_async_style.capitalization = pascal_case
55+
dotnet_naming_style.end_in_async_style.required_suffix = Async
56+
57+
# Use PascalCase for constant fields
58+
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = warning
59+
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
60+
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
61+
dotnet_naming_symbols.constant_fields.applicable_kinds = field
62+
dotnet_naming_symbols.constant_fields.required_modifiers = const
63+
# Use PascalCase for static members
64+
dotnet_naming_rule.static_members_should_be_pascal_case.severity = warning
65+
dotnet_naming_rule.static_members_should_be_pascal_case.symbols = static_members
66+
dotnet_naming_rule.static_members_should_be_pascal_case.style = pascal_case_style
67+
dotnet_naming_symbols.static_members.required_modifiers = static
68+
# Use camelCase for private fields
69+
dotnet_naming_rule.private_fields_should_be_camel_case.severity = warning
70+
dotnet_naming_rule.private_fields_should_be_camel_case.symbols = private_fields
71+
dotnet_naming_rule.private_fields_should_be_camel_case.style = camel_case_style
72+
dotnet_naming_symbols.private_fields.applicable_kinds = field
73+
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
74+
# Use PascalCase for methods and properties
75+
dotnet_naming_rule.methods_and_properties_must_be_pascal_case.severity = warning
76+
dotnet_naming_rule.methods_and_properties_must_be_pascal_case.symbols = method_and_property
77+
dotnet_naming_rule.methods_and_properties_must_be_pascal_case.style = pascal_case_style
78+
dotnet_naming_symbols.method_and_property.applicable_kinds = method,property
79+
# Use PascalCase for public members
80+
dotnet_naming_rule.public_members_must_be_capitalized.severity = warning
81+
dotnet_naming_rule.public_members_must_be_capitalized.symbols = public
82+
dotnet_naming_rule.public_members_must_be_capitalized.style = pascal_case_style
83+
dotnet_naming_symbols.public.applicable_kinds = property,method,field,event,delegate
84+
dotnet_naming_symbols.public.required_modifiers = public,internal,protected,protected_internal
85+
# Async methods must end in Async
86+
dotnet_naming_rule.async_methods_must_end_with_async.severity = warning
87+
dotnet_naming_rule.async_methods_must_end_with_async.symbols = async_method
88+
dotnet_naming_rule.async_methods_must_end_with_async.style = end_in_async_style
89+
dotnet_naming_symbols.async_method.applicable_kinds = method
90+
dotnet_naming_symbols.async_method.required_modifiers = async
91+
###############################
92+
# C# Coding Conventions #
93+
###############################
94+
[*.cs]
95+
# var preferences
96+
csharp_style_var_for_built_in_types = false:suggestion
97+
csharp_style_var_when_type_is_apparent = true:suggestion
98+
csharp_style_var_elsewhere = true:suggestion
99+
# Expression-bodied members
100+
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
101+
csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
102+
csharp_style_expression_bodied_operators = when_on_single_line:suggestion
103+
csharp_style_expression_bodied_properties = when_on_single_line:suggestion
104+
csharp_style_expression_bodied_indexers = when_on_single_line:suggestion
105+
csharp_style_expression_bodied_accessors = when_on_single_line:suggestion
106+
# Pattern matching preferences
107+
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
108+
csharp_style_pattern_matching_over_as_with_null_check = true:warning
109+
# Null-checking preferences
110+
csharp_style_throw_expression = true:suggestion
111+
csharp_style_conditional_delegate_call = true:suggestion
112+
# Modifier preferences
113+
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning
114+
# Expression-level preferences
115+
csharp_prefer_braces = true:suggestion
116+
csharp_style_deconstructed_variable_declaration = true:suggestion
117+
csharp_prefer_simple_default_expression = true:suggestion
118+
csharp_style_pattern_local_over_anonymous_function = true:suggestion
119+
csharp_style_inlined_variable_declaration = true:suggestion
120+
###############################
121+
# C# Formatting Rules #
122+
###############################
123+
# New line preferences
124+
csharp_new_line_before_open_brace = all
125+
csharp_new_line_before_else = true
126+
csharp_new_line_before_catch = true
127+
csharp_new_line_before_finally = true
128+
csharp_new_line_before_members_in_object_initializers = true
129+
csharp_new_line_before_members_in_anonymous_types = true
130+
csharp_new_line_between_query_expression_clauses = true
131+
# Indentation preferences
132+
csharp_indent_case_contents = true
133+
csharp_indent_switch_labels = true
134+
csharp_indent_labels = flush_left
135+
# Space preferences
136+
csharp_space_after_cast = false
137+
csharp_space_after_keywords_in_control_flow_statements = true
138+
csharp_space_between_method_call_parameter_list_parentheses = false
139+
csharp_space_between_method_declaration_parameter_list_parentheses = false
140+
csharp_space_between_parentheses = false
141+
csharp_space_before_colon_in_inheritance_clause = true
142+
csharp_space_after_colon_in_inheritance_clause = true
143+
csharp_space_around_binary_operators = before_and_after
144+
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
145+
csharp_space_between_method_call_name_and_opening_parenthesis = false
146+
csharp_space_between_method_call_empty_parameter_list_parentheses = false
147+
# Wrapping preferences
148+
csharp_preserve_single_line_statements = false
149+
csharp_preserve_single_line_blocks = true

NOTICE.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Runpath.Extensions.Logging.AzureEventHubs
2+
Copyright 2019 Experian plc

README.md

+78
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,80 @@
11
# Runpath.Extensions.Logging.AzureEventHubs
2+
23
Azure Event Hubs logger provider implementation for Microsoft.Extensions.Logging.
4+
5+
## Getting started
6+
7+
Grab the package from NuGet, which will install all dependencies.
8+
9+
`Install-Package Runpath.Extensions.Logging.AzureEventHubs`
10+
11+
## Usage
12+
13+
Extensive documentation for Microsoft.Extensions.Logging is available [here](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/) and should cover most general aspects of this library.
14+
15+
Once your Azure Event Hubs resource is configured in Azure, you can then add its details to your configuration. The options expose 4 values that you can later use:
16+
17+
```json
18+
{
19+
"Logging": {
20+
"AzureEventHubs": {
21+
"Endpoint": "sb://example.servicebus.windows.net",
22+
"EntityPath": "my-hub",
23+
"SharedAccessKeyName": "my-key",
24+
"SharedAccessKey": "..."
25+
}
26+
}
27+
}
28+
```
29+
30+
Add this logger provider to your logging builder, supplying a delegate that creates an `EventHubClient` to your specifications, for example:
31+
32+
```csharp
33+
var services = new ServiceCollection();
34+
35+
services.AddLogging(builder => builder.AddAzureEventHubs(options =>
36+
options.TryGetConnectionString(out string connectionString)
37+
? EventHubClient.CreateFromConnectionString(connectionString)
38+
: EventHubClient.CreateWithManagedServiceIdentity(options.Endpoint, options.EntityPath);
39+
));
40+
```
41+
42+
### `IAzureEventHubsLoggerFormatter`
43+
44+
The formatting of event data is controlled by an instance of this interface. A default implementation is supplied out of the box, which formats events as JSON:
45+
46+
```json
47+
{
48+
"Timestamp": "2019-07-11T08:53:37.772Z",
49+
"LogLevel": "Information",
50+
"Category": "MyApplication",
51+
"EventId": 0,
52+
"Message": "Application started.",
53+
"Exception": null
54+
}
55+
```
56+
57+
To implement your own custom format, create your own implementation of `IAzureEventHubsLoggerFormatter` and replace the default instance in your service collection.
58+
59+
Custom implementations will have access to external scope data, provided by `IExternalScopeProvider`. To consume this, use the `ForEachScope` method exposed by `IAzureEventHubsLoggerFormatter`.
60+
61+
### `IAzureEventHubsLoggerProcessor`
62+
63+
The processing of event data is controlled by an instance of this interface. A default implementation is supplied that implements a queue, offloads work to a background thread, and sends event data using batches.
64+
65+
The options expose 2 (optional) values to customise the thresholds and queuing logic of the default processor:
66+
67+
```json
68+
{
69+
"Logging": {
70+
"AzureEventHubs": {
71+
"QueueDepth": 1024,
72+
"QueueMode": "DropOldest"
73+
}
74+
}
75+
}
76+
```
77+
78+
`QueueDepth` must be a positive integer, and defaults to 1024. `QueueMode` can accept one of the values of [`BoundedChannelFullMode`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.channels.boundedchannelfullmode), and defaults to `DropOldest`.
79+
80+
To implement your own custom processing logic, create your own implementation of `IAzureEventHubsLoggerProcessor` and replace the default instance in your service collection.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29102.190
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runpath.Extensions.Logging.AzureEventHubs.Tests", "tests\Runpath.Extensions.Logging.AzureEventHubs.Tests\Runpath.Extensions.Logging.AzureEventHubs.Tests.csproj", "{34EF5237-9B25-4C53-A13F-DA81D7CFD103}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runpath.Extensions.Logging.AzureEventHubs", "src\Runpath.Extensions.Logging.AzureEventHubs\Runpath.Extensions.Logging.AzureEventHubs.csproj", "{339B5B29-D1ED-4780-83A3-17461099F550}"
9+
EndProject
10+
Global
11+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12+
Debug|Any CPU = Debug|Any CPU
13+
Release|Any CPU = Release|Any CPU
14+
EndGlobalSection
15+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16+
{34EF5237-9B25-4C53-A13F-DA81D7CFD103}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17+
{34EF5237-9B25-4C53-A13F-DA81D7CFD103}.Debug|Any CPU.Build.0 = Debug|Any CPU
18+
{34EF5237-9B25-4C53-A13F-DA81D7CFD103}.Release|Any CPU.ActiveCfg = Release|Any CPU
19+
{34EF5237-9B25-4C53-A13F-DA81D7CFD103}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{339B5B29-D1ED-4780-83A3-17461099F550}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{339B5B29-D1ED-4780-83A3-17461099F550}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{339B5B29-D1ED-4780-83A3-17461099F550}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{339B5B29-D1ED-4780-83A3-17461099F550}.Release|Any CPU.Build.0 = Release|Any CPU
24+
EndGlobalSection
25+
GlobalSection(SolutionProperties) = preSolution
26+
HideSolutionNode = FALSE
27+
EndGlobalSection
28+
GlobalSection(ExtensibilityGlobals) = postSolution
29+
SolutionGuid = {C59C9FD0-A789-4E0A-A768-9D364B60311C}
30+
EndGlobalSection
31+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using Microsoft.Azure.EventHubs;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.DependencyInjection.Extensions;
5+
using Microsoft.Extensions.Logging.Configuration;
6+
using Runpath.Extensions.Logging.AzureEventHubs;
7+
8+
// ReSharper disable once CheckNamespace
9+
namespace Microsoft.Extensions.Logging
10+
{
11+
public static class AzureEventHubLoggerFactoryExtensions
12+
{
13+
/// <summary>
14+
/// Adds a AzureEventHubs logger named 'AzureEventHubs' to the factory.
15+
/// </summary>
16+
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
17+
/// <param name="eventHubClientFactory"></param>
18+
public static ILoggingBuilder AddAzureEventHubs(this ILoggingBuilder builder,
19+
Func<AzureEventHubsLoggerOptions, EventHubClient> eventHubClientFactory)
20+
{
21+
if (builder is null)
22+
{
23+
throw new ArgumentNullException(nameof(builder));
24+
}
25+
26+
builder.AddConfiguration();
27+
28+
builder.Services.TryAddSingleton<IAzureEventHubsLoggerFormatter, DefaultAzureEventHubsLoggerFormatter>();
29+
builder.Services.TryAddSingleton<IAzureEventHubsLoggerProcessor, DefaultAzureEventHubsLoggerProcessor>();
30+
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, AzureEventHubsLoggerProvider>());
31+
builder.Services.RegisterProviderOptions<AzureEventHubsLoggerOptions, AzureEventHubsLoggerProvider>();
32+
builder.Services.Configure<AzureEventHubsLoggerOptions>(opts => opts.EventHubClientFactory = eventHubClientFactory);
33+
34+
return builder;
35+
}
36+
37+
/// <summary>
38+
/// Adds a AzureEventHubs logger named 'AzureEventHubs' to the factory.
39+
/// </summary>
40+
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
41+
/// <param name="eventHubClientFactory"></param>
42+
/// <param name="configure"></param>
43+
public static ILoggingBuilder AddAzureEventHubs(this ILoggingBuilder builder,
44+
Func<AzureEventHubsLoggerOptions, EventHubClient> eventHubClientFactory,
45+
Action<AzureEventHubsLoggerOptions> configure)
46+
{
47+
if (builder is null)
48+
{
49+
throw new ArgumentNullException(nameof(builder));
50+
}
51+
52+
if (configure is null)
53+
{
54+
throw new ArgumentNullException(nameof(configure));
55+
}
56+
57+
builder.AddAzureEventHubs(eventHubClientFactory);
58+
builder.Services.Configure(configure);
59+
60+
return builder;
61+
}
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using Microsoft.Extensions.Logging;
3+
4+
namespace Runpath.Extensions.Logging.AzureEventHubs
5+
{
6+
internal class AzureEventHubsLogger : ILogger
7+
{
8+
private readonly string name;
9+
private readonly IAzureEventHubsLoggerFormatter loggerFormatter;
10+
private readonly IAzureEventHubsLoggerProcessor loggerProcessor;
11+
12+
internal AzureEventHubsLogger(string name, IAzureEventHubsLoggerFormatter loggerFormatter, IAzureEventHubsLoggerProcessor loggerProcessor)
13+
{
14+
this.name = name ?? throw new ArgumentNullException(nameof(name));
15+
this.loggerFormatter = loggerFormatter ?? throw new ArgumentNullException(nameof(loggerFormatter));
16+
this.loggerProcessor = loggerProcessor ?? throw new ArgumentNullException(nameof(loggerProcessor));
17+
}
18+
19+
internal IExternalScopeProvider ScopeProvider { get; set; }
20+
21+
internal AzureEventHubsLoggerOptions Options { get; set; }
22+
23+
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
24+
{
25+
if (!IsEnabled(logLevel))
26+
{
27+
return;
28+
}
29+
30+
if (formatter is null)
31+
{
32+
throw new ArgumentNullException(nameof(formatter));
33+
}
34+
35+
var eventData = this.loggerFormatter.Format(this.name, logLevel, eventId, state, exception, formatter);
36+
37+
this.loggerProcessor.Process(eventData);
38+
}
39+
40+
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
41+
42+
public IDisposable BeginScope<TState>(TState state) => ScopeProvider?.Push(state) ?? NullScope.Instance;
43+
}
44+
}

0 commit comments

Comments
 (0)