Wiem, że to zły pomysł, ale ... Chcę programowo skonfigurować log4net od zera, bez pliku konfiguracyjnego. Pracuję nad prostą aplikacją do logowania dla mnie i mojego zespołu, która będzie używana dla kilku stosunkowo małych aplikacji dla działów, za które odpowiadamy. Chcę, aby wszystkie logowały się do tej samej bazy danych. Aplikacja rejestrująca to tylko otoka wokół log4net z wstępnie skonfigurowanym AdoNetAppender.
Wszystkie aplikacje są wdrożone za pomocą technologii ClickOnce, co stanowi mały problem z wdrażaniem pliku konfiguracyjnego. Jeśli plik konfiguracyjny był częścią projektu podstawowego, mógłbym ustawić jego właściwości do wdrożenia z zestawem. Ale jest częścią połączonej aplikacji, więc nie mam możliwości wdrożenia jej z główną aplikacją. (Jeśli to nieprawda, niech ktoś da mi znać).
Prawdopodobnie dlatego, że jest to zły pomysł, wydaje się, że nie ma zbyt wielu przykładowego kodu do programowej konfiguracji log4net od zera. Oto, co mam do tej pory.
Dim apndr As New AdoNetAppender()
apndr.CommandText = "INSERT INTO LOG_ENTRY (LOG_DTM, LOG_LEVEL, LOGGER, MESSAGE, PROGRAM, USER_ID, MACHINE, EXCEPTION) VALUES (@log_date, @log_level, @logger, @message, @program, @user, @machine, @exception)"
apndr.ConnectionString = connectionString
apndr.ConnectionType = "System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
apndr.CommandType = CommandType.Text
Dim logDate As New AdoNetAppenderParameter()
logDate.ParameterName = "@log_date"
logDate.DbType = DbType.DateTime
logDate.Layout = New RawTimeStampLayout()
apndr.AddParameter(logDate)
Dim logLevel As New AdoNetAppenderParameter()
logLevel.ParameterName = "@log_level"
'And so forth...
Po skonfigurowaniu wszystkich parametrów dla apndr
, najpierw spróbowałem tego ...
Dim hier As Hierarchy = DirectCast(LogManager.GetRepository(), Hierarchy)
hier.Root.AddAppender(apndr)
To nie zadziałało. Potem, jako strzał w ciemności, spróbowałem tego.
BasicConfigurator.Configure(apndr)
To też nie zadziałało. Czy ktoś ma jakieś dobre referencje na temat tego, jak programowo skonfigurować log4net od zera bez pliku konfiguracyjnego?
Odpowiedzi:
Jednym ze sposobów, w jaki zrobiłem to w przeszłości, jest dołączenie pliku konfiguracyjnego jako zasobu osadzonego i właśnie użyłem log4net.Config.Configure (Stream) .
W ten sposób mogłem użyć znanej mi składni konfiguracji i nie musiałem martwić się o wdrożenie pliku.
źródło
Oto przykładowa klasa, która tworzy konfigurację log4net całkowicie w kodzie. Powinienem wspomnieć, że tworzenie loggera metodą statyczną jest ogólnie postrzegane jako złe, ale w moim kontekście tego właśnie chciałem. Niezależnie od tego, możesz podzielić kod na swoje potrzeby.
using log4net; using log4net.Repository.Hierarchy; using log4net.Core; using log4net.Appender; using log4net.Layout; namespace dnservices.logging { public class Logger { private PatternLayout _layout = new PatternLayout(); private const string LOG_PATTERN = "%d [%t] %-5p %m%n"; public string DefaultPattern { get { return LOG_PATTERN; } } public Logger() { _layout.ConversionPattern = DefaultPattern; _layout.ActivateOptions(); } public PatternLayout DefaultLayout { get { return _layout; } } public void AddAppender(IAppender appender) { Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository(); hierarchy.Root.AddAppender(appender); } static Logger() { Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository(); TraceAppender tracer = new TraceAppender(); PatternLayout patternLayout = new PatternLayout(); patternLayout.ConversionPattern = LOG_PATTERN; patternLayout.ActivateOptions(); tracer.Layout = patternLayout; tracer.ActivateOptions(); hierarchy.Root.AddAppender(tracer); RollingFileAppender roller = new RollingFileAppender(); roller.Layout = patternLayout; roller.AppendToFile = true; roller.RollingStyle = RollingFileAppender.RollingMode.Size; roller.MaxSizeRollBackups = 4; roller.MaximumFileSize = "100KB"; roller.StaticLogFileName = true; roller.File = "dnservices.txt"; roller.ActivateOptions(); hierarchy.Root.AddAppender(roller); hierarchy.Root.Level = Level.All; hierarchy.Configured = true; } public static ILog Create() { return LogManager.GetLogger("dnservices"); } }
}
źródło
hierarchy.Configured = true;
co mnieBardziej zwięzłe rozwiązanie:
var layout = new PatternLayout("%-4timestamp [%thread] %-5level %logger %ndc - %message%newline"); var appender = new RollingFileAppender { File = "my.log", Layout = layout }; layout.ActivateOptions(); appender.ActivateOptions(); BasicConfigurator.Configure(appender);
Nie zapomnij wywołać metody ActivateOptions :
źródło
ActivateOptions()
zdecydowanie brakuje lub przynajmniej nie jest wystarczająco wskazany w dokumentacji.Jak mówi Jonathan , korzystanie z zasobu jest dobrym rozwiązaniem.
Jest to trochę restrykcyjne, ponieważ zawartość osadzonego zasobu zostanie naprawiona w czasie kompilacji. Mam komponent rejestrowania, który generuje XmlDocument z podstawową konfiguracją Log4Net, używając zmiennych zdefiniowanych jako appSettings (np. Nazwa pliku dla RollingFileAppender, domyślny poziom rejestrowania, może nazwa ciągu połączenia, jeśli chcesz używać AdoNetAppender). Następnie dzwonię,
log4net.Config.XmlConfigurator.Configure
aby skonfigurować Log4Net przy użyciu elementu głównego wygenerowanego XmlDocument.Następnie administratorzy mogą dostosować „standardową” konfigurację, modyfikując kilka ustawień aplikacji (zwykle poziom, nazwa pliku, ...) lub określić zewnętrzny plik konfiguracyjny, aby uzyskać większą kontrolę.
źródło
Nie mogę stwierdzić we fragmencie kodu pytania, czy „I tak dalej…” zawiera bardzo ważną funkcję apndr.ActivateOptions (), która jest wskazana w odpowiedzi Todda Stouta. Bez ActivateOptions () Appender jest nieaktywny i nie zrobi nic, co mogłoby wyjaśnić, dlaczego zawodzi.
źródło
Trochę za późno na przyjęcie. Ale tutaj jest minimalna konfiguracja, która działała dla mnie.
Przykładowa klasa
public class Bar { private readonly ILog log = LogManager.GetLogger(typeof(Bar)); public void DoBar() { log.Info("Logged"); } }
Minimalna konfiguracja śledzenia log4net (w teście NUnit)
[Test] public void Foo() { var tracer = new TraceAppender(); var hierarchy = (Hierarchy)LogManager.GetRepository(); hierarchy.Root.AddAppender(tracer); var patternLayout = new PatternLayout {ConversionPattern = "%m%n"}; tracer.Layout = patternLayout; hierarchy.Configured = true; var bar = new Bar(); bar.DoBar(); }
Drukuje do detektora śledzenia
Namespace+Bar: Logged
źródło
Dr Netjes ma to do programowego ustawiania łańcucha połączenia:
// Get the Hierarchy object that organizes the loggers log4net.Repository.Hierarchy.Hierarchy hier = log4net.LogManager.GetLoggerRepository() as log4net.Repository.Hierarchy.Hierarchy; if (hier != null) { //get ADONetAppender log4net.Appender.ADONetAppender adoAppender = (log4net.Appender.ADONetAppender)hier.GetLogger("MyProject", hier.LoggerFactory).GetAppender("ADONetAppender"); if (adoAppender != null) { adoAppender.ConnectionString = System.Configuration.ConfigurationSettings.AppSettings["MyConnectionString"]; adoAppender.ActivateOptions(); //refresh settings of appender } }
źródło
// Osadziłem trzy pliki konfiguracyjne jako zasób osadzony i uzyskuję do nich dostęp w następujący sposób:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Resources; using System.IO; namespace Loader { class Program { private static log4net.ILog CustomerLog = log4net.LogManager.GetLogger("CustomerLogging"); private static log4net.ILog OrderLog = log4net.LogManager.GetLogger("OrderLogging"); private static log4net.ILog DetailsLog = log4net.LogManager.GetLogger("OrderDetailLogging"); static void Main(string[] args) { // array of embedded log4net config files string[] configs = { "Customer.config", "Order.config", "Detail.config"}; foreach (var config in configs) { // build path to assembly config StringBuilder sb = new StringBuilder(); sb.Append(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name); sb.Append("."); sb.Append(config); // convert to a stream Stream configStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(sb.ToString()); // configure logger with ocnfig stream log4net.Config.XmlConfigurator.Configure(configStream); // test logging CustomerLog.Info("Begin logging with: " + config); OrderLog.Info("Begin logging with: " + config); DetailsLog.Info("Begin logging with: " + config); for (int iX = 0; iX < 10; iX++) { CustomerLog.Info("iX=" + iX); OrderLog.Info("iX=" + iX); DetailsLog.Info("iX=" + iX); } CustomerLog.Info("Ending logging with: " + config); OrderLog.Info("Ending logging with: " + config); DetailsLog.Info("Ending logging with: " + config); } } } }
źródło
To dziwne, że
BasicConfigurator.Configure(apndr)
nie zadziałało. W moim przypadku spełniło swoje zadanie ... Ale tak czy inaczej, oto odpowiedź - powinieneś był napisaćhier.Configured = true;
(kod C #) po zakończeniu całej konfiguracji.źródło
Skończyło się na tym:
http://www.mikebevers.be/blog/2010/09/log4net-custom-adonetappender/
Po 4 godzinach majstrowania przy konfiguracji i coraz bardziej frustracji.
Mam nadzieję, że to komuś pomoże.
źródło
Oto przykład zupy do orzechów, w jaki sposób można tworzyć i używać
AdoNetAdapter
całkowicie w kodzie, całkowicie bez żadnegoApp.config
pliku (nawet dlaCommon.Logging
). Śmiało, usuń to!Ma to dodatkową zaletę, że jest odporny na aktualizacje w ramach nowych konwencji nazewnictwa , w których nazwa zestawu odzwierciedla teraz wersję. (
Common.Logging.Log4Net1213
itp.)[SQL]
CREATE TABLE [Log]( [Id] [int] IDENTITY(1,1) NOT NULL, [Date] [datetime] NOT NULL, [Thread] [varchar](255) NOT NULL, [Level] [varchar](20) NOT NULL, [Source] [varchar](255) NOT NULL, [Message] [varchar](max) NOT NULL, [Exception] [varchar](max) NOT NULL )
[Główny]
Imports log4net Imports log4net.Core Imports log4net.Layout Imports log4net.Config Imports log4net.Appender Module Main Sub Main() Dim oLogger As ILog Dim sInput As String Dim iOops As Integer BasicConfigurator.Configure(New DbAppender) oLogger = LogManager.GetLogger(GetType(Main)) Console.Write("Command: ") Do Try sInput = Console.ReadLine.Trim Select Case sInput.ToUpper Case "QUIT" : Exit Do Case "OOPS" : iOops = String.Empty Case Else : oLogger.Info(sInput) End Select Catch ex As Exception oLogger.Error(ex.Message, ex) End Try Console.Clear() Console.Write("Command: ") Loop End Sub End Module
[DbAppender]
Imports log4net Imports log4net.Core Imports log4net.Layout Imports log4net.Appender Imports log4net.Repository.Hierarchy Public Class DbAppender Inherits AdoNetAppender Public Sub New() MyBase.BufferSize = 1 MyBase.CommandText = Me.CommandText Me.Parameters.ForEach(Sub(Parameter As DbParameter) MyBase.AddParameter(Parameter) End Sub) Me.ActivateOptions() End Sub Protected Overrides Function CreateConnection(ConnectionType As Type, ConnectionString As String) As IDbConnection Return MyBase.CreateConnection(GetType(System.Data.SqlClient.SqlConnection), "Data Source=(local);Initial Catalog=Logger;Persist Security Info=True;User ID=username;Password=password") End Function Private Overloads ReadOnly Property CommandText As String Get Dim _ sColumns, sValues As String sColumns = Join(Me.Parameters.Select(Function(P As DbParameter) P.DbColumn).ToArray, ",") sValues = Join(Me.Parameters.Select(Function(P As DbParameter) P.ParameterName).ToArray, ",") Return String.Format(COMMAND_TEXT, sColumns, sValues) End Get End Property Private ReadOnly Property Parameters As List(Of DbParameter) Get Parameters = New List(Of DbParameter) Parameters.Add(Me.LogDate) Parameters.Add(Me.Thread) Parameters.Add(Me.Level) Parameters.Add(Me.Source) Parameters.Add(Me.Message) Parameters.Add(Me.Exception) End Get End Property Private ReadOnly Property LogDate As DbParameter Get Return New DbParameter("Date", DbType.Date, 0, New DbPatternLayout("%date{yyyy-MM-dd HH:mm:ss.fff}")) End Get End Property Private ReadOnly Property Thread As DbParameter Get Return New DbParameter("Thread", DbType.String, 255, New DbPatternLayout("%thread")) End Get End Property Private ReadOnly Property Level As DbParameter Get Return New DbParameter("Level", DbType.String, 50, New DbPatternLayout("%level")) End Get End Property Private ReadOnly Property Source As DbParameter Get Return New DbParameter("Source", DbType.String, 255, New DbPatternLayout("%logger.%M()")) End Get End Property Private ReadOnly Property Message As DbParameter Get Return New DbParameter("Message", DbType.String, 4000, New DbPatternLayout("%message")) End Get End Property Private ReadOnly Property Exception As DbParameter Get Return New DbParameter("Exception", DbType.String, 2000, New DbExceptionLayout) End Get End Property Private Const COMMAND_TEXT As String = "INSERT INTO Log ({0}) VALUES ({1})" End Class
[DbParameter]
Imports log4net Imports log4net.Core Imports log4net.Layout Imports log4net.Appender Imports log4net.Repository.Hierarchy Public Class DbParameter Inherits AdoNetAppenderParameter Private ReadOnly Name As String Public Sub New(Name As String, Type As DbType, Size As Integer, Layout As ILayout) With New RawLayoutConverter Me.Layout = .ConvertFrom(Layout) End With Me.Name = Name.Replace("@", String.Empty) Me.ParameterName = String.Format("@{0}", Me.Name) Me.DbType = Type Me.Size = Size End Sub Public ReadOnly Property DbColumn As String Get Return String.Format("[{0}]", Me.Name) End Get End Property End Class
[DbPatternLayout]
Imports log4net Imports log4net.Core Imports log4net.Layout Imports log4net.Appender Imports log4net.Repository.Hierarchy Public Class DbPatternLayout Inherits PatternLayout Public Sub New(Pattern As String) Me.ConversionPattern = Pattern Me.ActivateOptions() End Sub End Class
[DbExceptionLayout]
Imports log4net Imports log4net.Core Imports log4net.Layout Imports log4net.Appender Imports log4net.Repository.Hierarchy Public Class DbExceptionLayout Inherits ExceptionLayout Public Sub New() Me.ActivateOptions() End Sub End Class
źródło
„Rozwiązanie dla Vb.Net
Private Shared EscanerLog As log4net.ILog = log4net.LogManager.GetLogger("Log4Net.Config") Public Sub New(ByVal sIDSesion As String) Dim sStream As Stream Dim JsText As String Using reader As New StreamReader((GetType(ClsGestorLogsTraza).Assembly).GetManifestResourceStream("Comun.Log4Net.Config")) JsText = reader.ReadToEnd() sStream = GenerateStreamFromString(JsText) log4net.Config.XmlConfigurator.Configure(sStream) End Using End Sub Public Function GenerateStreamFromString(ByVal s As String) As Stream Dim stream = New MemoryStream() Dim writer = New StreamWriter(stream) writer.Write(s) writer.Flush() stream.Position = 0 Return stream End Function Public Function StreamFromResource(ByVal sFilename As String) As Stream Dim nAssembly As System.Reflection.Assembly = System.Reflection.Assembly.GetExecutingAssembly() Dim s As Stream = nAssembly.GetManifestResourceStream(System.Reflection.MethodBase.GetCurrentMethod.DeclaringType, sFilename) Return s End Function
źródło