I am consuming a Class Library which is built using .NET Core. The Library has only one public class with a constructor accepting ILoggerFactory and another overloaded constructor accepting ILogger. But I am consuming this library in a console app built using .NET Framework 4.7. My console app uses Log4Net for logging. But I need to inject the instance of ILogger or ILoggerFactory so that the class library log's error based on my log.config settings.
I tried as below
ILogger logger = new LoggerFactory().CreateLogger();
var contentManager = new ContentManager(logger);
But I don't know how to pass the Log4Net instance to the LoggerFactory.
private static ILog _log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
ILogger logger = new LoggerFactory().CreateLogger<ContentManager>();
var contentManager = new ContentManager(logger);
contentManager.Run();
}
I don't find anything getting logged and ContentManager doesn't throw any exceptions. I know that the ILogger instance doesn't know anything about Log4Net from my console app, how I need to pass the Log4Net instance? Need help on this.
Try the Adapter Pattern to facilitate delegation to Log4Net. I'm not familiar with Log4Net but I think you'll understand this example:
class MyLoggerAdapter : Library.ILogger
{
private readonly ILog _delegation;
public MyLoggerAdapter(ILog delegation)
{
_delegation = delegation;
}
public void ILogger.Debug(string msg, params object[] p)
{
_delegation.Debug(msg, p);
}
}
Then in your code you can do this:
private static ILog _log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
MyLoggerAdapter logAdapter = new MyLoggerAdapter(_log);
var contentManager = new ContentManager(logAdapter);
contentManager.Run();
}
The answer from Dan S is a great general answer with good example. To implement this specifically for log4net you could use a class like this:
public class Log4netAdapter<TCategoryName> : ILogger<TCategoryName>
{
private readonly ILog _logger = LogManager.GetLogger(typeof(TCategoryName));
public IDisposable BeginScope<TState>(TState state)
{
return null; // log4net does not have scope capability
}
public bool IsEnabled(LogLevel logLevel)
{
switch (logLevel)
{
case LogLevel.Trace:
case LogLevel.Debug:
return _logger.IsDebugEnabled;
case LogLevel.Information:
return _logger.IsInfoEnabled;
case LogLevel.Warning:
return _logger.IsWarnEnabled;
case LogLevel.Error:
return _logger.IsErrorEnabled;
case LogLevel.Critical:
return _logger.IsFatalEnabled;
case LogLevel.None:
default:
return false;
}
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel)) return;
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
var message = formatter(state, exception);
if (!string.IsNullOrEmpty(message) || exception != null)
{
switch (logLevel)
{
case LogLevel.None:
break;
case LogLevel.Trace:
case LogLevel.Debug:
_logger.Debug(message, exception);
break;
case LogLevel.Information:
_logger.Info(message, exception);
break;
case LogLevel.Warning:
_logger.Warn(message, exception);
break;
case LogLevel.Error:
_logger.Error(message, exception);
break;
case LogLevel.Critical:
_logger.Fatal(message, exception);
break;
}
}
}
}
Then reference the packages log4net and Microsoft.Extensions.Logging (or Microsoft.Extensions.Logging.Abstractions if you are creating this in a library).
To use the adapter when instantiating a class using Microsoft.Extensions.Logging (and your project is not using dependency injection):
var logger = new Log4netAdapter<NewClassLibrary>();
var newClassLibrary = new NewClassLibrary(logger);
I'm trying to figure out how to get more detailed logs out of Identity Server 4, especially if things go wrong. I have read their documentation here http://docs.identityserver.io/en/latest/topics/logging.html However, I can't seem to get it to work. For one I don't use serilog and I have the IS4 running on a server I don't have remote access to so console logging is not going to work for me.
As such, I've tried to inject my own custom logging library that I have with the following:
public class Temp : IDisposable
{
public void Dispose() { }
}
public class CustomLogger : ILogger
{
private readonly IDatabaseLoggerService _databaseLoggerService;
public CustomLogger(IDatabaseLoggerService databaseLoggerService)
{
_databaseLoggerService = databaseLoggerService;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if(exception == null) return;
_databaseLoggerService.Fatal(new LogErrorDetails
{
Exception = exception,
Message = "LIS - " + formatter
});
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public IDisposable BeginScope<TState>(TState state)
{
return new Temp();
}
}
public class CustomLoggerProvider : ILoggerProvider
{
private readonly IDatabaseLoggerService _databaseLoggerService;
public CustomLoggerProvider(IDatabaseLoggerService databaseLoggerService)
{
_databaseLoggerService = databaseLoggerService;
}
public void Dispose() { }
public ILogger CreateLogger(string categoryName)
{
return new CustomLogger(_databaseLoggerService);
}
}
As you can see its fairly straightforward because at this point I don't need it polished, just need it working, and I'll work on the proper implementation of it later once I have the sample working.
Now to set this up I did it in the Startup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//code emmited
var serviceProvider = app.ApplicationServices;
var dbLogger = serviceProvider.GetService<IDatabaseLoggerService>();
loggerFactory.AddProvider(new CustomLoggerProvider(dbLogger));
}
However, I don't see any logs about it, the only thing I saw was the occasional anti-forgery token error that now pops up and that is it:
at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenSerializer.Deserialize(String serializedToken) at Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.GetCookieTokenDoesNotThrow(HttpContext httpContext)
What I would like to see is if a token request was successful or if it failed and why, but I'm not seeing any of it.
How would I go to accomplish higher level of logging for Identity Server 4?
You should be able to add your logger right into the WebHostBuilder giving you probably most insightful logs.
You can start of by creating an extension method to keep things nice and clean. We just need to add a singleton of our logger factory to the DI.
public static IWebHostBuilder UseCustomLogger(this IWebHostBuilder builder, CustomLogger logger = null, bool dispose = false)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
builder.ConfigureServices(collection =>
collection.AddSingleton<ILoggerFactory>(services => new CustomLoggerFactory(logger, dispose)));
return builder;
}
Factory itself would be very simple.
public class CustomLoggerFactory : ILoggerFactory
{
private readonly CustomLoggerProvider _provider;
public CustomLoggerFactory(ILogger logger = null, bool dispose = false)
{
_provider = new CustomLoggerProvider(logger, dispose);
}
public void Dispose()
{
_provider.Dispose();
}
public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName)
{
return _provider.CreateLogger(categoryName);
}
}
You can then add your logger in Program.cs:
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseCustomLogger();
Lastly, looking at your logger implementation - you will need to create instance of IDatabaseLoggerService in the factory so that the logger can be created. Similar approach can of course be used with the ASP.NET Core DI.
How can I use .NET Core's default dependency injection in Hangfire?
I am new to Hangfire and searching for an example which works with ASP.NET Core.
See full example on GitHub https://github.com/gonzigonz/HangfireCore-Example.
Live site at http://hangfirecore.azurewebsites.net/
Make sure you have the Core version of Hangfire:
dotnet add package Hangfire.AspNetCore
Configure your IoC by defining a JobActivator. Below is the config for use with the default asp.net core container service:
public class HangfireActivator : Hangfire.JobActivator
{
private readonly IServiceProvider _serviceProvider;
public HangfireActivator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public override object ActivateJob(Type type)
{
return _serviceProvider.GetService(type);
}
}
Next register hangfire as a service in the Startup.ConfigureServices method:
services.AddHangfire(opt =>
opt.UseSqlServerStorage("Your Hangfire Connection string"));
Configure hangfire in the Startup.Configure method. In relationship to your question, the key is to configure hangfire to use the new HangfireActivator we just defined above. To do so you will have to provide hangfire with the IServiceProvider and this can be achieved by just adding it to the list of parameters for the Configure method. At runtime, DI will providing this service for you:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
IServiceProvider serviceProvider)
{
...
// Configure hangfire to use the new JobActivator we defined.
GlobalConfiguration.Configuration
.UseActivator(new HangfireActivator(serviceProvider));
// The rest of the hangfire config as usual.
app.UseHangfireServer();
app.UseHangfireDashboard();
}
When you enqueue a job, use the registered type which usually is your interface. Don't use a concrete type unless you registered it that way. You must use the type registered with your IoC else Hangfire won't find it.
For Example say you've registered the following services:
services.AddScoped<DbManager>();
services.AddScoped<IMyService, MyService>();
Then you could enqueue DbManager with an instantiated version of the class:
BackgroundJob.Enqueue(() => dbManager.DoSomething());
However you could not do the same with MyService. Enqueuing with an instantiated version would fail because DI would fail as only the interface is registered. In this case you would enqueue like this:
BackgroundJob.Enqueue<IMyService>( ms => ms.DoSomething());
DoritoBandito's answer is incomplete or deprecated.
public class EmailSender {
public EmailSender(IDbContext dbContext, IEmailService emailService)
{
_dbContext = dbContext;
_emailService = emailService;
}
}
Register services:
services.AddTransient<IDbContext, TestDbContext>();
services.AddTransient<IEmailService, EmailService>();
Enqueue:
BackgroundJob.Enqueue<EmailSender>(x => x.Send(13, "Hello!"));
Source:
http://docs.hangfire.io/en/latest/background-methods/passing-dependencies.html
Note: if you want a full sample, see my blog post on this.
All of the answers in this thread are wrong/incomplete/outdated. Here's an example with ASP.NET Core 3.1 and Hangfire.AspnetCore 1.7.
Client:
//...
using Hangfire;
// ...
public class Startup
{
// ...
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddHangfire(config =>
{
// configure hangfire per your requirements
});
}
}
public class SomeController : ControllerBase
{
private readonly IBackgroundJobClient _backgroundJobClient;
public SomeController(IBackgroundJobClient backgroundJobClient)
{
_backgroundJobClient = backgroundJobClient;
}
[HttpPost("some-route")]
public IActionResult Schedule([FromBody] SomeModel model)
{
_backgroundJobClient.Schedule<SomeClass>(s => s.Execute(model));
}
}
Server (same or different application):
{
//...
services.AddScoped<ISomeDependency, SomeDependency>();
services.AddHangfire(hangfireConfiguration =>
{
// configure hangfire with the same backing storage as your client
});
services.AddHangfireServer();
}
public interface ISomeDependency { }
public class SomeDependency : ISomeDependency { }
public class SomeClass
{
private readonly ISomeDependency _someDependency;
public SomeClass(ISomeDependency someDependency)
{
_someDependency = someDependency;
}
// the function scheduled in SomeController
public void Execute(SomeModel someModel)
{
}
}
As far as I am aware, you can use .net cores dependency injection the same as you would for any other service.
You can use a service which contains the jobs to be executed, which can be executed like so
var jobId = BackgroundJob.Enqueue(x => x.SomeTask(passParamIfYouWish));
Here is an example of the Job Service class
public class JobService : IJobService
{
private IClientService _clientService;
private INodeServices _nodeServices;
//Constructor
public JobService(IClientService clientService, INodeServices nodeServices)
{
_clientService = clientService;
_nodeServices = nodeServices;
}
//Some task to execute
public async Task SomeTask(Guid subject)
{
// Do some job here
Client client = _clientService.FindUserBySubject(subject);
}
}
And in your projects Startup.cs you can add a dependency as normal
services.AddTransient< IClientService, ClientService>();
Not sure this answers your question or not
Currently, Hangfire is deeply integrated with Asp.Net Core. Install Hangfire.AspNetCore to set up the dashboard and DI integration automatically. Then, you just need to define your dependencies using ASP.NET core as always.
If you are trying to quickly set up Hangfire with ASP.NET Core (tested in ASP.NET Core 2.2) you can also use Hangfire.MemoryStorage. All the configuration can be performed in Startup.cs:
using Hangfire;
using Hangfire.MemoryStorage;
public void ConfigureServices(IServiceCollection services)
{
services.AddHangfire(opt => opt.UseMemoryStorage());
JobStorage.Current = new MemoryStorage();
}
protected void StartHangFireJobs(IApplicationBuilder app, IServiceProvider serviceProvider)
{
app.UseHangfireServer();
app.UseHangfireDashboard();
//TODO: move cron expressions to appsettings.json
RecurringJob.AddOrUpdate<SomeJobService>(
x => x.DoWork(),
"* * * * *");
RecurringJob.AddOrUpdate<OtherJobService>(
x => x.DoWork(),
"0 */2 * * *");
}
public void Configure(IApplicationBuilder app, IServiceProvider serviceProvider)
{
StartHangFireJobs(app, serviceProvider)
}
Of course, everything is store in memory and it is lost once the application pool is recycled, but it is a quick way to see that everything works as expected with minimal configuration.
To switch to SQL Server database persistence, you should install Hangfire.SqlServer package and simply configure it instead of the memory storage:
services.AddHangfire(opt => opt.UseSqlServerStorage(Configuration.GetConnectionString("Default")));
I had to start HangFire in main function. This is how I solved it:
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var serviceScope = host.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
try
{
var liveDataHelper = services.GetRequiredService<ILiveDataHelper>();
var justInitHangfire = services.GetRequiredService<IBackgroundJobClient>();
//This was causing an exception (HangFire is not initialized)
RecurringJob.AddOrUpdate(() => liveDataHelper.RePopulateAllConfigDataAsync(), Cron.Daily());
// Use the context here
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "Can't start " + nameof(LiveDataHelper));
}
}
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
Actually there is an easy way for dependency injection based job registration.
You just need to use the following code in your Startup:
public class Startup {
public void Configure(IApplicationBuilder app)
{
var factory = app.ApplicationServices
.GetService<IServiceScopeFactory>();
GlobalConfiguration.Configuration.UseActivator(
new Hangfire.AspNetCore.AspNetCoreJobActivator(factory));
}
}
However i personally wanted a job self registration including on demand jobs (recurring jobs which are never executed, except by manual trigger on hangfire dashboard), which was a little more complex then just that. I was (for example) facing issues with the job service activation, which is why i decided to share most of my implementation code.
//I wanted an interface to declare my jobs, including the job Id.
public interface IBackgroundJob {
string Id { get; set; }
void Invoke();
}
//I wanted to retrieve the jobs by id. Heres my extension method for that:
public static IBackgroundJob GetJob(
this IServiceProvider provider,
string jobId) => provider
.GetServices<IBackgroundJob>()
.SingleOrDefault(j => j.Id == jobId);
//Now i needed an invoker for these jobs.
//The invoker is basically an example of a dependency injected hangfire job.
internal class JobInvoker {
public JobInvoker(IServiceScopeFactory factory) {
Factory = factory;
}
public IServiceScopeFactory Factory { get; }
public void Invoke(string jobId)
{
//hangfire jobs should always be executed within their own scope.
//The default AspNetCoreJobActivator should technically already do that.
//Lets just say i have trust issues.
using (var scope = Factory.CreateScope())
{
scope.ServiceProvider
.GetJob(jobId)?
.Invoke();
}
}
//Now i needed to tell hangfire to use these jobs.
//Reminder: The serviceProvider is in IApplicationBuilder.ApplicationServices
public static void RegisterJobs(IServiceProvider serviceProvider) {
var factory = serviceProvider.GetService();
GlobalConfiguration.Configuration.UseActivator(new Hangfire.AspNetCore.AspNetCoreJobActivator(factory));
var manager = serviceProvider.GetService<IRecurringJobManager>();
var config = serviceProvider.GetService<IConfiguration>();
var jobs = serviceProvider.GetServices<IBackgroundJob>();
foreach (var job in jobs) {
var jobConfig = config.GetJobConfig(job.Id);
var schedule = jobConfig?.Schedule; //this is a cron expression
if (String.IsNullOrWhiteSpace(schedule))
schedule = Cron.Never(); //this is an on demand job only!
manager.AddOrUpdate(
recurringJobId: job.Id,
job: GetJob(job.Id),
cronExpression: schedule);
}
//and last but not least...
//My Method for creating the hangfire job with injected job id
private static Job GetJob(string jobId)
{
var type = typeof(JobInvoker);
var method = type.GetMethod("Invoke");
return new Job(
type: type,
method: method,
args: jobId);
}
Using the above code i was able to create hangfire job services with full dependency injection support. Hope it helps someone.
Use the below code for Hangfire configuration
using eForms.Core;
using Hangfire;
using Hangfire.SqlServer;
using System;
using System.ComponentModel;
using System.Web.Hosting;
namespace eForms.AdminPanel.Jobs
{
public class JobManager : IJobManager, IRegisteredObject
{
public static readonly JobManager Instance = new JobManager();
//private static readonly TimeSpan ZeroTimespan = new TimeSpan(0, 0, 10);
private static readonly object _lockObject = new Object();
private bool _started;
private BackgroundJobServer _backgroundJobServer;
private JobManager()
{
}
public int Schedule(JobInfo whatToDo)
{
int result = 0;
if (!whatToDo.IsRecurring)
{
if (whatToDo.Delay == TimeSpan.Zero)
int.TryParse(BackgroundJob.Enqueue(() => Run(whatToDo.JobId, whatToDo.JobType.AssemblyQualifiedName)), out result);
else
int.TryParse(BackgroundJob.Schedule(() => Run(whatToDo.JobId, whatToDo.JobType.AssemblyQualifiedName), whatToDo.Delay), out result);
}
else
{
RecurringJob.AddOrUpdate(whatToDo.JobType.Name, () => RunRecurring(whatToDo.JobType.AssemblyQualifiedName), Cron.MinuteInterval(whatToDo.Delay.TotalMinutes.AsInt()));
}
return result;
}
[DisplayName("Id: {0}, Type: {1}")]
[HangFireYearlyExpirationTime]
public static void Run(int jobId, string jobType)
{
try
{
Type runnerType;
if (!jobType.ToType(out runnerType)) throw new Exception("Provided job has undefined type");
var runner = runnerType.CreateInstance<JobRunner>();
runner.Run(jobId);
}
catch (Exception ex)
{
throw new JobException($"Error while executing Job Id: {jobId}, Type: {jobType}", ex);
}
}
[DisplayName("{0}")]
[HangFireMinutelyExpirationTime]
public static void RunRecurring(string jobType)
{
try
{
Type runnerType;
if (!jobType.ToType(out runnerType)) throw new Exception("Provided job has undefined type");
var runner = runnerType.CreateInstance<JobRunner>();
runner.Run(0);
}
catch (Exception ex)
{
throw new JobException($"Error while executing Recurring Type: {jobType}", ex);
}
}
public void Start()
{
lock (_lockObject)
{
if (_started) return;
if (!AppConfigSettings.EnableHangFire) return;
_started = true;
HostingEnvironment.RegisterObject(this);
GlobalConfiguration.Configuration
.UseSqlServerStorage("SqlDbConnection", new SqlServerStorageOptions { PrepareSchemaIfNecessary = false })
//.UseFilter(new HangFireLogFailureAttribute())
.UseLog4NetLogProvider();
//Add infinity Expiration job filter
//GlobalJobFilters.Filters.Add(new HangFireProlongExpirationTimeAttribute());
//Hangfire comes with a retry policy that is automatically set to 10 retry and backs off over several mins
//We in the following remove this attribute and add our own custom one which adds significant backoff time
//custom logic to determine how much to back off and what to to in the case of fails
// The trick here is we can't just remove the filter as you'd expect using remove
// we first have to find it then save the Instance then remove it
try
{
object automaticRetryAttribute = null;
//Search hangfire automatic retry
foreach (var filter in GlobalJobFilters.Filters)
{
if (filter.Instance is Hangfire.AutomaticRetryAttribute)
{
// found it
automaticRetryAttribute = filter.Instance;
System.Diagnostics.Trace.TraceError("Found hangfire automatic retry");
}
}
//Remove default hangefire automaticRetryAttribute
if (automaticRetryAttribute != null)
GlobalJobFilters.Filters.Remove(automaticRetryAttribute);
//Add custom retry job filter
GlobalJobFilters.Filters.Add(new HangFireCustomAutoRetryJobFilterAttribute());
}
catch (Exception) { }
_backgroundJobServer = new BackgroundJobServer(new BackgroundJobServerOptions
{
HeartbeatInterval = new System.TimeSpan(0, 1, 0),
ServerCheckInterval = new System.TimeSpan(0, 1, 0),
SchedulePollingInterval = new System.TimeSpan(0, 1, 0)
});
}
}
public void Stop()
{
lock (_lockObject)
{
if (_backgroundJobServer != null)
{
_backgroundJobServer.Dispose();
}
HostingEnvironment.UnregisterObject(this);
}
}
void IRegisteredObject.Stop(bool immediate)
{
Stop();
}
}
}
Admin Job Manager
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
if (Core.AppConfigSettings.EnableHangFire)
{
JobManager.Instance.Start();
new SchedulePendingSmsNotifications().Schedule(new Core.JobInfo() { JobId = 0, JobType = typeof(SchedulePendingSmsNotifications), Delay = TimeSpan.FromMinutes(1), IsRecurring = true });
}
}
protected void Application_End(object sender, EventArgs e)
{
if (Core.AppConfigSettings.EnableHangFire)
{
JobManager.Instance.Stop();
}
}
}
We are running a Silverlight application running on IIS 7.5 using Forms authentication to sign in users. We have a RIA service which is protected using RequiresAuthentication, and an ErrorHandlingService and a LoggingService which is not.
Recently we seem to be spammed with requests to our ErrorHandlingService - in the order of 50 pr. second - producing the following output in the IIS log:
2013-10-01 11:19:12 xxx.xxx.xxx.xxx POST
...ErrorService.svc
- 80 - xxx.xxx.xxx.xxx Mozilla/4.0+(compatible;+MSIE+8.0;+Windows+NT+6.1;+Trident/4.0;+SLCC2;+.NET+CLR+2.0.50727;+.NET+CLR+3.5.30729;+.NET+CLR+3.0.30729;+Media+Center+PC+6.0;+.NET4.0C;+.NET4.0E;+InfoPath.2;+MS-RTC+LM+8)
403 6 5 15
Now a little more detail on the setup:
The ErrorHandlingService has a single method accepting an Exception wrapper class, which will log the exception info into a table in the database, and the LoggingService accepts a LogMessage and same exception wrapper and logs the message to a file using NLog.
Whenever an exception occurs on the protected service, the silverlight client will first call the ErrorHandlingService to record the exception, and afterwards the LoggingService to log it to file.
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ErrorHandlingService : IErrorHandlingService
{
public string RecordError(ExceptionTransport error)
{
if (error == null)
{
return null;
}
var hex = HandMadeException.CreateFrom(error);
var res = DiagnosticsHandler.DefaultInstance.HandleException(hex);
var fb = res.GetFeedBack(DatabaseSink.FeedBackEntryId);
var dbId = "";
if (fb != null)
{
dbId = fb.ToString();
}
return dbId;
}
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class LoggingService : ILoggingService
{
private static NLog.Logger _logger = NLog.LogManager.GetLogger("Trace");
public void LogError(LogMessage message, ExceptionTransport error)
{
string log = message.Message;
if (error != null)
{
log = string.Format("{0}: {1}", log, error.ToString());
}
switch (message.Level)
{
case LogLevel.Trace:
_logger.Trace(log);
break;
case LogLevel.Debug:
_logger.Debug(log);
break;
case LogLevel.Info:
_logger.Info(log);
break;
case LogLevel.Warning:
_logger.Warn(log);
break;
case LogLevel.Error:
_logger.Error(log);
break;
case LogLevel.Fatal:
_logger.Fatal(log);
break;
}
}
}
public class ErrorHandlingService : ErrorService, IErrorHandler
{
private LogHandlingService _loggingService = new LogHandlingService();
public ErrorHandlingService()
: base(WcfWebUtility.CreateFactory<IErrorHandlingService>("xxxxErrorHandlingService"))
{
}
public bool HandleError(Exception error)
{
base.RecordError(error, OnErrorRecorded, false);
}
private void LogError(Exception error, string errorId, bool showUserMessage)
{
_loggingService.Log(string.Format("Error '{0}' occured on Ecofleet v. '{1}' for user '{2}'", errorId, ProductVersion.GetEcofleetVersion(), AutologUser.Current), Autolog.DiagnosticsService.Logging.LogLevel.Error, error);
}
private void OnErrorRecorded(ErrorResult result)
{
bool showUserMessage = false;
if (result.UserState is bool)
{
showUserMessage = (bool)result.UserState;
}
LogError(result.ErrorRecorded, result.ErrorId, showUserMessage);
}
... Shortened for brevity
}
So this is what's happening:
In the IIS log output we see a ton of requests to the
ErrorHandlingService, but none to the LoggingService.
Nothing is entered into the database suggesting that the error parameter is
null.
The application works fine, and no users are complaining about
errors. I would expect any user getting 55 errors pr. second to
either contact the support department, or at the very least log
off...
The client IP address seems to remain the same most of the
time.
So is it possibly an (malicious) attack? Or could it be a client version discrepancy (if a new version is deployed and a client doesn't download it, would it be running an old client version against a new server?)? Or something else ... ?
When I include a structuremap dependency resolver in my global.asax for signalR any calls to client functions no longer arrive at the browser although I can see them if in the logging pipeline.
Here's an extract of my global.asax:
var container = ObjectFactory.Container;
// Now configure SignalR, MVC, ASP.Net and SharpRepository
GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule());
GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());
// TODO: Work out why this breaks the connection between the server and the client.
GlobalHost.DependencyResolver =
ObjectFactory.GetInstance<IDependencyResolver>();
RouteTable.Routes.MapHubs();
DependencyResolver.SetResolver(
new StructureMapDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver =
new StructureMapDependencyResolver(container);
RepositoryDependencyResolver.SetDependencyResolver(
new SharpRepository.Ioc.StructureMap.StructureMapDependencyResolver(container));
Here the implementation of the structuremap resolver:
public class StructureMapSignalRDependencyResolver
: DefaultDependencyResolver
{
private readonly IContainer _container;
public StructureMapSignalRDependencyResolver(
IContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
_container = container;
}
public override object GetService(Type serviceType)
{
object result = null;
try
{
result =
!serviceType.IsAbstract &&
!serviceType.IsInterface &&
serviceType.IsClass
? _container.GetInstance(serviceType)
: (_container.TryGetInstance(serviceType)
?? base.GetService(serviceType));
}
catch (Exception ex)
{
Tracing.Error(
"[StructureMapSignalRDependencyResolver]",
Tracing.SerializeException(ex));
}
if (result == null)
{
Tracing.Information(
"[StructureMapSignalRDependencyResolver]",
"Could retrieve object of type {0}",serviceType.ToString());
}
return result;
}
public override IEnumerable<object> GetServices(
Type serviceType)
{
IEnumerable<object> result = null;
try
{
result = _container.GetAllInstances(serviceType)
.Cast<object>().Concat(
base.GetServices(serviceType));
}
catch (Exception ex)
{
Tracing.Error(
"[StructureMapSignalRDependencyResolver]",
Tracing.SerializeException(ex));
}
if (result == null)
{
Tracing.Information(
"[StructureMapSignalRDependencyResolver]",
"Could retrieve object of type {0}", serviceType.ToString());
}
return result;
}
public override void Register(Type serviceType,
Func<object> activator)
{
Tracing.Information(
"[StructureMapSignalRDependencyResolver]",
"Registering object of type {0}",
serviceType.ToString());
base.Register(serviceType, activator);
}
public override void Register(Type serviceType,
IEnumerable<Func<object>> activators)
{
Tracing.Information(
"[StructureMapSignalRDependencyResolver]",
"Registering object of type {0}",
serviceType.ToString());
base.Register(serviceType, activators);
}
}
Here is the implementation of StructureMap Registry (there are many more which include the application assemblies).
[RegistryOrder(Order = 6)]
public class SignalRRegistry : Registry
{
public SignalRRegistry()
{
For<IDependencyResolver>().Singleton()
.Use<StructureMapSignalRDependencyResolver>();
For<IHubConnectionContext>().Singleton()
.Use(GlobalHost.ConnectionManager
.GetHubContext<BOSSHub>().Clients);
}
//public void Configure()
//{
// GlobalHost.DependencyResolver =
// ObjectFactory.GetInstance<IDependencyResolver>();
// RouteTable.Routes.MapHubs();
//}
}
I've rolled back the Hub class so that it no longer has an dependencies; the output from WhatDoIHave seems to have some entries for Microsoft.AspNet.SignalR but without any concrete relationships.
I wondering if I missed a step in my registry ? I get no exceptions anywhere; it just stops working, if I comment out the GlobalHost.DependencyResolver line from global.asax all is well.
I hope someone could share with me a DI implementation for signalR that is working for them.
Many Thanks
UPDATE: Thanks for sorting out the bad formatting for me - appreciate that.
UPDATE: I've put together a small test project which shows the issue. I was surprised if I'm honest I was able to reproduce it as the application that I'm working on it pretty big and complex with lot's of StructureMap stuff going on. I've uploaded into GitHub : https://github.com/johnk27stars/SignalRTest.git - Thanks to anyone who could spare a minute to take a look.
After some honest advice from Jeremy Miller we switched to AutoFac and it working fine.