Skip to content

Commit 9401db4

Browse files
committed
initial commit
0 parents  commit 9401db4

20 files changed

+835
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.vscode
2+
bin
3+
obj
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.1</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<ProjectReference Include="..\CoreHelpers.TaskLogging.Abstractions\CoreHelpers.TaskLogging.Abstractions.csproj" />
10+
</ItemGroup>
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
13+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
14+
</ItemGroup>
15+
</Project>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using CoreHelpers.TaskLogging;
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace CoreHelpers.Extensions.Logging.Tasks
7+
{
8+
internal class TaskLogger : ILogger
9+
{
10+
private ITaskLoggerFactory _taskLoggerFactory;
11+
12+
private ITaskLogger? _currentTaskLogger = null;
13+
private bool _lastLogWasAnError = false;
14+
15+
public TaskLogger(ITaskLoggerFactory taskLoggerFactory)
16+
{
17+
_taskLoggerFactory = taskLoggerFactory;
18+
}
19+
20+
// By default the task logging framework does not care about
21+
// unknown scopes
22+
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
23+
{
24+
// check if we are interested
25+
var castedState = state as TaskLoggerState;
26+
if (castedState == null)
27+
return null;
28+
29+
// just in case a task logger is active flush
30+
if (_currentTaskLogger != null)
31+
{
32+
_currentTaskLogger.Dispose();
33+
_lastLogWasAnError = false;
34+
}
35+
36+
// check if we need to announce the task
37+
if (!castedState.IsTaskAnnounced)
38+
{
39+
castedState.TaskId = _taskLoggerFactory.AnnounceTask(castedState.TaskType, castedState.TaskSource, castedState.TaskWorker).GetAwaiter().GetResult();
40+
castedState.IsTaskAnnounced = true;
41+
}
42+
43+
// set a new task logger
44+
_currentTaskLogger = _taskLoggerFactory.CreateTaskLogger(castedState.TaskId);
45+
46+
// ensure the task is running now
47+
_taskLoggerFactory.UpdateTaskStatus(castedState.TaskId, TaskStatus.Running).GetAwaiter().GetResult();
48+
49+
// done
50+
return new TaskLoggerScope(castedState, this);
51+
}
52+
53+
// All levels are eanbled
54+
public bool IsEnabled(LogLevel logLevel) => true;
55+
56+
// allow to log
57+
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
58+
{
59+
if (_currentTaskLogger == null)
60+
return;
61+
62+
// adjust the log level
63+
var taskLogLevel = TaskLogLevel.Information;
64+
if (logLevel == LogLevel.Error || logLevel == LogLevel.Critical)
65+
taskLogLevel = TaskLogLevel.Error;
66+
67+
// log
68+
_currentTaskLogger.Log(taskLogLevel, formatter(state, exception), exception);
69+
70+
// remember the exception
71+
if (logLevel == LogLevel.Error)
72+
_lastLogWasAnError = true;
73+
else
74+
_lastLogWasAnError = false;
75+
76+
}
77+
78+
// ensure we write all backe
79+
public void DisposeScope(TaskLoggerState state, TaskLoggerScope scope)
80+
{
81+
if (_currentTaskLogger != null)
82+
{
83+
// write all messages back
84+
_currentTaskLogger.Dispose();
85+
_currentTaskLogger = null;
86+
87+
// ensure the task is finished now
88+
_taskLoggerFactory.UpdateTaskStatus(state.TaskId, _lastLogWasAnError ? TaskStatus.Failed : TaskStatus.Succeed).GetAwaiter().GetResult();
89+
90+
// reset the exception
91+
_lastLogWasAnError = false;
92+
}
93+
}
94+
}
95+
}
96+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using Microsoft.Extensions.Logging;
3+
using Microsoft.Extensions.DependencyInjection;
4+
5+
namespace CoreHelpers.Extensions.Logging.Tasks
6+
{
7+
public static class TaskLoggerExtension
8+
{
9+
public static ILoggingBuilder AddTaskLogger(this ILoggingBuilder builder)
10+
{
11+
builder.Services.AddSingleton<ILoggerProvider, TaskLoggerProvider>();
12+
return builder;
13+
}
14+
15+
public static IDisposable? BeginTaskScope(this ILogger logger, string taskId)
16+
=> logger.BeginScope<TaskLoggerState>(new TaskLoggerState() { TaskId = taskId, IsTaskAnnounced = true });
17+
18+
public static IDisposable? BeginNewTaskScope(this ILogger logger, string taskType, string taskSource, string taskWorker)
19+
=> logger.BeginScope<TaskLoggerState>(new TaskLoggerState() { TaskId = string.Empty, TaskType = taskType, TaskSource = taskSource, TaskWorker = taskSource, IsTaskAnnounced = false });
20+
}
21+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using CoreHelpers.TaskLogging;
3+
using Microsoft.Extensions.Logging;
4+
5+
namespace CoreHelpers.Extensions.Logging.Tasks
6+
{
7+
internal class TaskLoggerProvider : ILoggerProvider
8+
{
9+
private ITaskLoggerFactory _taskLoggerFactory;
10+
11+
public TaskLoggerProvider(ITaskLoggerFactory taskLoggerFactory)
12+
{
13+
_taskLoggerFactory = taskLoggerFactory;
14+
}
15+
16+
public ILogger CreateLogger(string categoryName)
17+
{
18+
return new TaskLogger(_taskLoggerFactory);
19+
}
20+
21+
public void Dispose()
22+
{
23+
24+
}
25+
}
26+
}
27+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
namespace CoreHelpers.Extensions.Logging.Tasks
3+
{
4+
internal class TaskLoggerScope : IDisposable
5+
{
6+
private TaskLogger _taskLogger;
7+
private TaskLoggerState _taskState;
8+
9+
public TaskLoggerScope(TaskLoggerState taskState, TaskLogger taskLogger)
10+
{
11+
_taskLogger = taskLogger;
12+
_taskState = taskState;
13+
}
14+
15+
public void Dispose()
16+
{
17+
_taskLogger.DisposeScope(_taskState, this);
18+
}
19+
}
20+
}
21+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
namespace CoreHelpers.Extensions.Logging.Tasks
3+
{
4+
internal class TaskLoggerState
5+
{
6+
public string TaskId { get; set; } = string.Empty;
7+
8+
public string TaskType {get; set;} = string.Empty;
9+
public string TaskSource { get; set; } = string.Empty;
10+
public string TaskWorker { get; set; } = string.Empty;
11+
public bool IsTaskAnnounced { get; set; } = false;
12+
}
13+
}
14+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.1</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<RootNamespace>CoreHelpers.TaskLogging</RootNamespace>
7+
</PropertyGroup>
8+
9+
</Project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
namespace CoreHelpers.TaskLogging
3+
{
4+
public enum TaskLogLevel {
5+
Information,
6+
Error
7+
}
8+
9+
public interface ITaskLogger : IDisposable
10+
{
11+
void Log(TaskLogLevel logLevel, string message, Exception? exception, Func<string, Exception?, string> formatter);
12+
13+
void Log(TaskLogLevel logLevel, string message, Exception? exception);
14+
}
15+
}
16+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
5+
namespace CoreHelpers.TaskLogging
6+
{
7+
public enum TaskStatus
8+
{
9+
Pending,
10+
Running,
11+
Failed,
12+
Succeed
13+
}
14+
15+
public interface ITaskLoggerFactory
16+
{
17+
/// <summary>
18+
/// Creates a new task logger for an individual task. All subsequent loggings
19+
/// with this task logger will be stored in the same context
20+
/// </summary>
21+
/// <param name="uniqueTaskId"></param>
22+
/// <returns></returns>
23+
ITaskLogger CreateTaskLogger(string uniqueTaskId);
24+
25+
/// <summary>
26+
/// Announces a new task in the state pending to the logging frameowrk. Only announced
27+
/// tasks can be used in a task logger by calling CreateTaskLogger
28+
/// </summary>
29+
/// <param name="taskType"></param>
30+
/// <param name="taskSource"></param>
31+
/// <param name="taskWorker"></param>
32+
/// <returns></returns>
33+
Task<string> AnnounceTask(string taskType, string taskSource, string taskWorker);
34+
35+
/// <summary>
36+
/// Announces a new task in the state pending to the logging framework including required
37+
/// meta data usable in the frontend. Only announced tasks can be used in a task logger by
38+
/// calling CreateTaskLogger
39+
/// </summary>
40+
/// <param name="taskType"></param>
41+
/// <param name="taskSource"></param>
42+
/// <param name="taskWorker"></param>
43+
/// <param name="metaData"></param>
44+
/// <returns></returns>
45+
Task<string> AnnounceTask(string taskType, string taskSource, string taskWorker, IDictionary<string, string> metaData);
46+
47+
/// <summary>
48+
/// Update the task status of a given task
49+
/// </summary>
50+
/// <param name="taskId"></param>
51+
/// <param name="taskStatus"></param>
52+
/// <returns></returns>
53+
Task UpdateTaskStatus(string taskId, TaskStatus taskStatus);
54+
}
55+
}
56+

0 commit comments

Comments
 (0)