public static IDisposable OpenGroup(this IActivityMonitor @this, LogLevel level, string text, Exception ex, [CallerFilePath] string fileName = null, [CallerLineNumber] int lineNumber = 0) { if (@this.ShouldLogGroup(level, fileName, lineNumber)) { return(@this.UnfilteredOpenGroup(new ActivityMonitorGroupData(level | LogLevel.IsFiltered, null, text, @this.NextLogTime(), ex, null, fileName, lineNumber))); } return(@this.UnfilteredOpenGroup(new ActivityMonitorGroupData())); }
static internal IDisposable Start(ActivityMonitor.DependentToken token, IActivityMonitor monitor, string fileName, int lineNumber) { string msg = token.FormatStartMessage(); if (token.Topic != null) { string currentTopic = token.Topic; monitor.SetTopic(token.Topic, fileName, lineNumber); var g = monitor.UnfilteredOpenGroup(ActivityMonitor.Tags.StartDependentActivity, LogLevel.Info, null, msg, monitor.NextLogTime(), null, fileName, lineNumber); return(Util.CreateDisposableAction(() => { g.Dispose(); monitor.SetTopic(currentTopic, fileName, lineNumber); })); } return(monitor.UnfilteredOpenGroup(ActivityMonitor.Tags.StartDependentActivity, LogLevel.Info, null, msg, monitor.NextLogTime(), null, fileName, lineNumber)); }
/// <summary> /// Launches one or more dependent activities (thanks to a delegate) that will use the current monitor's topic. /// This creates a new <see cref="ActivityMonitor.DependentToken"/> and opens a group that wraps the execution of the <paramref name="dependentLauncher"/>. /// </summary> /// <param name="dependentLauncher">Must create and launch dependent activities that should use the created token.</param> /// <returns>A dependent token.</returns> public void Launch(Action <ActivityMonitor.DependentToken> dependentLauncher) { if (dependentLauncher == null) { throw new ArgumentNullException(nameof(dependentLauncher)); } string msg; var t = ActivityMonitor.DependentToken.CreateWithMonitorTopic(_monitor, true, out msg); using (_monitor.UnfilteredOpenGroup(ActivityMonitor.Tags.CreateDependentActivity, LogLevel.Info, null, msg, t.CreationDate, null, _fileName, _lineNumber)) { dependentLauncher(t); _monitor.CloseGroup(_monitor.NextLogTime(), "Success."); } }
void IActivityMonitorClient.OnOpenGroup(IActivityLogGroup group) { Debug.Assert(group.GroupLevel != LogLevel.None, "A client never sees a filtered group."); Debug.Assert(group.Depth > 0, "Depth is 1-based."); // Make sure the index is available. // This handles the case where this ClientBridge has been added to the Monitor.Output // after the opening of Groups: we must not trigger a Close on the final monitor for them. int idx = group.Depth; while (idx > _openedGroups.Count) { _openedGroups.Add(false); } // By using here our array of boolean to track filtered opened groups against the target, we avoid useless // solicitation (and marshaling when crossing application domains). // Note: If the group has already been filtered out by extension methods (group.GroupLevel == LogLevel.None), // we do not see it here. Checking the LogLevelFilter is ok. if (((group.GroupLevel & LogLevel.IsFiltered) == 0 && !_applyTargetFilterToUnfilteredLogs) || (int)GetActualTargetFilter().Group <= (int)group.MaskedGroupLevel) { _targetMonitor.UnfilteredOpenGroup(group.GroupTags, group.GroupLevel, null, group.GroupText, group.LogTime, group.Exception, group.FileName, group.LineNumber); _openedGroups[idx - 1] = true; } else { _openedGroups[idx - 1] = false; } }
void Run() { try { int streamVersion = _reader.ReadInt32(); if (_interProcess) { _server.DisposeLocalCopyOfClientHandle(); } for (; ;) { var e = LogEntry.Read(_reader, streamVersion, out bool badEndOfStream); if (e == null || badEndOfStream) { _endFlag = badEndOfStream ? LogReceiverEndStatus.MissingEndMarker : LogReceiverEndStatus.Normal; break; } switch (e.LogType) { case LogEntryType.Line: { if (_monitor.ShouldLogLine(e.LogLevel, e.Tags, out var finalTags)) { var d = new ActivityMonitorLogData(e.LogLevel | LogLevel.IsFiltered, finalTags, e.Text, CKException.CreateFrom(e.Exception), e.FileName, e.LineNumber); d.SetExplicitLogTime(e.LogTime); _monitor.UnfilteredLog(ref d); } break; } case LogEntryType.OpenGroup: { ActivityMonitorLogData d; if (_monitor.ShouldLogLine(e.LogLevel, e.Tags, out var finalTags)) { d = new ActivityMonitorLogData(e.LogLevel | LogLevel.IsFiltered, finalTags, e.Text, CKException.CreateFrom(e.Exception), e.FileName, e.LineNumber); d.SetExplicitLogTime(e.LogTime); } else { d = default; } _monitor.UnfilteredOpenGroup(ref d); } break; case LogEntryType.CloseGroup: _monitor.CloseGroup(e.Conclusions, e.LogTime); break; } } } catch (Exception ex) { _endFlag = LogReceiverEndStatus.Error; _monitor.UnfilteredLog(LogLevel.Fatal, null, "While receiving pipe logs.", ex); } }
/// <summary> /// Replays this monitor's content into another monitor. /// </summary> /// <param name="replay">The target monitor. Can not be null.</param> /// <param name="monitor">Optional monitor (nothing is logged when null).</param> public void Replay(IActivityMonitor replay, IActivityMonitor?monitor = null) { using (monitor?.OpenInfo($"Replaying activity from '{MonitorId}'.")) { int nbMissing = 0; int nbTotal = 0; using (var page = ReadFirstPage(1024)) { foreach (ParentedLogEntry e in page.Entries) { ++nbTotal; LogLevel level = e.Entry.LogLevel; if (e.IsMissing) { ++nbMissing; level = LogLevel.Trace; } switch (e.Entry.LogType) { case LogEntryType.Line: var d = new ActivityMonitorLogData(level, e.Entry.Tags, e.Entry.Text, CKException.CreateFrom(e.Entry.Exception), e.Entry.FileName, e.Entry.LineNumber); d.SetExplicitLogTime(e.Entry.LogTime); replay.UnfilteredLog(ref d); break; case LogEntryType.OpenGroup: d = new ActivityMonitorLogData(level, e.Entry.Tags, e.Entry.Text, CKException.CreateFrom(e.Entry.Exception), e.Entry.FileName, e.Entry.LineNumber); d.SetExplicitLogTime(e.Entry.LogTime); replay.UnfilteredOpenGroup(ref d); break; case LogEntryType.CloseGroup: replay.CloseGroup(e.Entry.Conclusions, e.Entry.LogTime); break; } } page.ForwardPage(); } monitor?.CloseGroup($"Replayed {nbTotal} entries ({nbMissing} missing)."); } }
/// <summary> /// Replays this monitor's content into another monitor. /// </summary> /// <param name="replay">The target monitor. Can not be null.</param> /// <param name="m">Optional monitor (nothing is logged when null).</param> public void Replay(IActivityMonitor replay, IActivityMonitor m = null) { using (m != null ? m.OpenGroup(LogLevel.Info, string.Format("Replaying activity from '{0}'.", MonitorId), null) : null) { int nbMissing = 0; int nbTotal = 0; using (var page = ReadFirstPage(1024)) { foreach (ParentedLogEntry e in page.Entries) { ++nbTotal; LogLevel level = e.Entry.LogLevel; if (e.IsMissing) { ++nbMissing; level = LogLevel.Trace; } switch (e.Entry.LogType) { case LogEntryType.Line: replay.UnfilteredLog(e.Entry.Tags, level, e.Entry.Text, e.Entry.LogTime, CKException.CreateFrom(e.Entry.Exception), e.Entry.FileName, e.Entry.LineNumber); break; case LogEntryType.OpenGroup: replay.UnfilteredOpenGroup(e.Entry.Tags, level, null, e.Entry.Text, e.Entry.LogTime, CKException.CreateFrom(e.Entry.Exception), e.Entry.FileName, e.Entry.LineNumber); break; case LogEntryType.CloseGroup: replay.CloseGroup(e.Entry.LogTime, e.Entry.Conclusions); break; } } page.ForwardPage(); } if (m != null) { m.CloseGroup(String.Format("Replayed {0} entries ({1} missing).", nbTotal, nbMissing)); } } }
/// <summary> /// Opens a group regardless of <see cref="IActivityMonitor.ActualFilter">ActualFilter</see> level. /// <see cref="CloseGroup"/> must be called in order to close the group, and/or the returned object must be disposed (both safely can be called: /// the group is closed on the first action, the second one is ignored). /// </summary> /// <param name="this">This <see cref="IActivityMonitor"/>.</param> /// <param name="tags">Tags (from <see cref="ActivityMonitor.Tags"/>) to associate to the log. It will be union-ed with current <see cref="IActivityMonitor.AutoTags">AutoTags</see>.</param> /// <param name="level">Log level. The <see cref="LogLevel.None"/> level is used to open a filtered group. See remarks.</param> /// <param name="getConclusionText">Optional function that will be called on group closing.</param> /// <param name="text">Text to log (the title of the group). Null text is valid and considered as <see cref="String.Empty"/> or assigned to the <see cref="Exception.Message"/> if it exists.</param> /// <param name="logTime"> /// Time of the log entry. /// You can use <see cref="DateTimeStamp.UtcNow"/> or <see cref="ActivityMonitorExtension.NextLogTime">IActivityMonitor.NextLogTime()</see> extension method. /// </param> /// <param name="ex">Optional exception associated to the group.</param> /// <param name="fileName">The source code file name from which the group is opened.</param> /// <param name="lineNumber">The line number in the source from which the group is opened.</param> /// <returns>A disposable object that can be used to close the group.</returns> /// <remarks> /// <para> /// Opening a group does not change the current <see cref="IActivityMonitor.MinimalFilter">MinimalFilter</see>, except when /// opening a <see cref="LogLevel.Fatal"/> or <see cref="LogLevel.Error"/> group: in such case, the Filter is automatically /// sets to <see cref="LogFilter.Debug"/> to capture all potential information inside the error group. /// </para> /// <para> /// Changes to the monitor's current Filter or AutoTags that occur inside a group are automatically restored to their original values when the group is closed. /// This behavior guaranties that a local modification (deep inside unknown called code) does not impact caller code: groups are a way to easily isolate such /// configuration changes. /// </para> /// <para> /// Note that this automatic configuration restoration works even if the group is filtered (when the <paramref name="level"/> is None). /// </para> /// </remarks> static public IDisposable UnfilteredOpenGroup(this IActivityMonitor @this, CKTrait tags, LogLevel level, Func <string> getConclusionText, string text, DateTimeStamp logTime, Exception ex, [CallerFilePath] string fileName = null, [CallerLineNumber] int lineNumber = 0) { return(@this.UnfilteredOpenGroup(new ActivityMonitorGroupData(level, tags, text, logTime, ex, getConclusionText, fileName, lineNumber))); }
static internal IDisposable Start( ActivityMonitor.DependentToken token, IActivityMonitor monitor, string fileName, int lineNumber ) { string msg = token.FormatStartMessage(); if( token.Topic != null ) { string currentTopic = token.Topic; monitor.SetTopic( token.Topic, fileName, lineNumber ); var g = monitor.UnfilteredOpenGroup( ActivityMonitor.Tags.StartDependentActivity, LogLevel.Info, null, msg, monitor.NextLogTime(), null, fileName, lineNumber ); return Util.CreateDisposableAction( () => { g.Dispose(); monitor.SetTopic( currentTopic, fileName, lineNumber ); } ); } return monitor.UnfilteredOpenGroup( ActivityMonitor.Tags.StartDependentActivity, LogLevel.Info, null, msg, monitor.NextLogTime(), null, fileName, lineNumber ); }
internal IDisposableGroup InitializeAndSend(Exception exception, CKTrait tags, string text) { Debug.Assert(!IsRejected); Initialize(text, exception, tags, Monitor.NextLogTime()); return(Monitor.UnfilteredOpenGroup(this)); }