1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 // this strange construct is to globally enable/disable `Trace` methods using single #define here; 10 // normally each file calling `Trace` should have it defined either on its own or globally in csproj/command line; 11 #if DEBUG 12 // uncomment line below to enable tracing 13 //#define TRACE_ENABLED 14 #endif 15 16 using System; 17 using System.Collections.Generic; 18 using Antmicro.Renode.Core; 19 using Antmicro.Renode.Peripherals; 20 using Antmicro.Renode.Utilities; 21 using System.Threading; 22 using System.Runtime.CompilerServices; 23 using System.IO; 24 using System.Collections.Concurrent; 25 using Antmicro.Migrant; 26 using Antmicro.Migrant.Hooks; 27 using Antmicro.Renode.Exceptions; 28 using Antmicro.Renode.Utilities.Collections; 29 using System.Diagnostics; 30 using System.Text; 31 using System.Linq; 32 using Antmicro.Renode.Debugging; 33 34 namespace Antmicro.Renode.Logging 35 { 36 public static class Logger 37 { AddBackend(ILoggerBackend backend, string name, bool overwrite = false)38 public static void AddBackend(ILoggerBackend backend, string name, bool overwrite = false) 39 { 40 backendNames.AddOrUpdate(name, backend, (key, value) => 41 { 42 if(!overwrite) 43 { 44 throw new RecoverableException(string.Format("Backend with name '{0}' already exists", key)); 45 } 46 value.Dispose(); 47 return backend; 48 }); 49 levels[new BackendSourceIdPair(backend, -1)] = backend.GetLogLevel(); 50 foreach(var level in backend.GetCustomLogLevels()) 51 { 52 levels[new BackendSourceIdPair(backend, level.Key)] = level.Value; 53 } 54 UpdateMinimumLevel(); 55 backends.Add(backend); 56 } 57 RemoveBackend(ILoggerBackend backend)58 public static void RemoveBackend(ILoggerBackend backend) 59 { 60 foreach(var level in levels.Where(pair => pair.Key.backend == backend).ToList()) 61 { 62 levels.TryRemove(level.Key, out var _); 63 } 64 UpdateMinimumLevel(); 65 backends.Remove(backend); 66 backend.Dispose(); 67 } 68 GetBackends()69 public static IDictionary<string, ILoggerBackend> GetBackends() 70 { 71 return backendNames; 72 } 73 Dispose()74 public static void Dispose() 75 { 76 Flush(); 77 foreach(var backend in backends.Items) 78 { 79 backend.Dispose(); 80 } 81 backends.Clear(); 82 backendNames.Clear(); 83 } 84 SetLogLevel(ILoggerBackend backend, LogLevel level, int sourceId)85 public static void SetLogLevel(ILoggerBackend backend, LogLevel level, int sourceId) 86 { 87 levels[new BackendSourceIdPair(backend, sourceId)] = level; 88 UpdateMinimumLevel(); 89 } 90 Log(LogLevel type, string message, params object[] args)91 public static void Log(LogLevel type, string message, params object[] args) 92 { 93 LogAs(null, type, message, args); 94 } 95 Log(LogLevel type, string message)96 public static void Log(LogLevel type, string message) 97 { 98 LogAs(null, type, message); 99 } 100 Log(LogLevel type, string message, object arg1)101 public static void Log(LogLevel type, string message, object arg1) 102 { 103 LogAs(null, type, message, arg1); 104 } 105 Log(LogLevel type, string message, object arg1, object arg2)106 public static void Log(LogLevel type, string message, object arg1, object arg2) 107 { 108 LogAs(null, type, message, arg1, arg2); 109 } 110 Log(LogLevel type, string message, object arg1, object arg2, object arg3)111 public static void Log(LogLevel type, string message, object arg1, object arg2, object arg3) 112 { 113 LogAs(null, type, message, arg1, arg2, arg3); 114 } 115 Error(string message)116 public static void Error(string message) 117 { 118 LogAs(null, LogLevel.Error, message); 119 } 120 Warning(string message)121 public static void Warning(string message) 122 { 123 LogAs(null, LogLevel.Warning, message); 124 } 125 Info(string message)126 public static void Info(string message) 127 { 128 LogAs(null, LogLevel.Info, message); 129 } 130 Debug(string message)131 public static void Debug(string message) 132 { 133 LogAs(null, LogLevel.Debug, message); 134 } 135 Noisy(string message)136 public static void Noisy(string message) 137 { 138 LogAs(null, LogLevel.Noisy, message); 139 } 140 ErrorLog(this IEmulationElement e, string message, params object[] args)141 public static void ErrorLog(this IEmulationElement e, string message, params object[] args) 142 { 143 LogAs(e, LogLevel.Error, message, args); 144 } 145 ErrorLog(this IEmulationElement e, string message)146 public static void ErrorLog(this IEmulationElement e, string message) 147 { 148 LogAs(e, LogLevel.Error, message); 149 } 150 ErrorLog(this IEmulationElement e, string message, object arg1)151 public static void ErrorLog(this IEmulationElement e, string message, object arg1) 152 { 153 LogAs(e, LogLevel.Error, message, arg1); 154 } 155 ErrorLog(this IEmulationElement e, string message, object arg1, object arg2)156 public static void ErrorLog(this IEmulationElement e, string message, object arg1, object arg2) 157 { 158 LogAs(e, LogLevel.Error, message, arg1, arg2); 159 } 160 ErrorLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3)161 public static void ErrorLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3) 162 { 163 LogAs(e, LogLevel.Error, message, arg1, arg2, arg3); 164 } 165 WarningLog(this IEmulationElement e, string message, params object[] args)166 public static void WarningLog(this IEmulationElement e, string message, params object[] args) 167 { 168 LogAs(e, LogLevel.Warning, message, args); 169 } 170 WarningLog(this IEmulationElement e, string message)171 public static void WarningLog(this IEmulationElement e, string message) 172 { 173 LogAs(e, LogLevel.Warning, message); 174 } 175 WarningLog(this IEmulationElement e, string message, object arg1)176 public static void WarningLog(this IEmulationElement e, string message, object arg1) 177 { 178 LogAs(e, LogLevel.Warning, message, arg1); 179 } 180 WarningLog(this IEmulationElement e, string message, object arg1, object arg2)181 public static void WarningLog(this IEmulationElement e, string message, object arg1, object arg2) 182 { 183 LogAs(e, LogLevel.Warning, message, arg1, arg2); 184 } 185 WarningLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3)186 public static void WarningLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3) 187 { 188 LogAs(e, LogLevel.Warning, message, arg1, arg2, arg3); 189 } 190 InfoLog(this IEmulationElement e, string message, params object[] args)191 public static void InfoLog(this IEmulationElement e, string message, params object[] args) 192 { 193 LogAs(e, LogLevel.Info, message, args); 194 } 195 InfoLog(this IEmulationElement e, string message)196 public static void InfoLog(this IEmulationElement e, string message) 197 { 198 LogAs(e, LogLevel.Info, message); 199 } 200 InfoLog(this IEmulationElement e, string message, object arg1)201 public static void InfoLog(this IEmulationElement e, string message, object arg1) 202 { 203 LogAs(e, LogLevel.Info, message, arg1); 204 } 205 InfoLog(this IEmulationElement e, string message, object arg1, object arg2)206 public static void InfoLog(this IEmulationElement e, string message, object arg1, object arg2) 207 { 208 LogAs(e, LogLevel.Info, message, arg1, arg2); 209 } 210 InfoLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3)211 public static void InfoLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3) 212 { 213 LogAs(e, LogLevel.Info, message, arg1, arg2, arg3); 214 } 215 DebugLog(this IEmulationElement e, string message, params object[] args)216 public static void DebugLog(this IEmulationElement e, string message, params object[] args) 217 { 218 LogAs(e, LogLevel.Debug, message, args); 219 } 220 DebugLog(this IEmulationElement e, string message)221 public static void DebugLog(this IEmulationElement e, string message) 222 { 223 LogAs(e, LogLevel.Debug, message); 224 } 225 DebugLog(this IEmulationElement e, string message, object arg1)226 public static void DebugLog(this IEmulationElement e, string message, object arg1) 227 { 228 LogAs(e, LogLevel.Debug, message, arg1); 229 } 230 DebugLog(this IEmulationElement e, string message, object arg1, object arg2)231 public static void DebugLog(this IEmulationElement e, string message, object arg1, object arg2) 232 { 233 LogAs(e, LogLevel.Debug, message, arg1, arg2); 234 } 235 DebugLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3)236 public static void DebugLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3) 237 { 238 LogAs(e, LogLevel.Debug, message, arg1, arg2, arg3); 239 } 240 NoisyLog(this IEmulationElement e, string message, params object[] args)241 public static void NoisyLog(this IEmulationElement e, string message, params object[] args) 242 { 243 LogAs(e, LogLevel.Noisy, message, args); 244 } 245 NoisyLog(this IEmulationElement e, string message)246 public static void NoisyLog(this IEmulationElement e, string message) 247 { 248 LogAs(e, LogLevel.Noisy, message); 249 } 250 NoisyLog(this IEmulationElement e, string message, object arg1)251 public static void NoisyLog(this IEmulationElement e, string message, object arg1) 252 { 253 LogAs(e, LogLevel.Noisy, message, arg1); 254 } 255 NoisyLog(this IEmulationElement e, string message, object arg1, object arg2)256 public static void NoisyLog(this IEmulationElement e, string message, object arg1, object arg2) 257 { 258 LogAs(e, LogLevel.Noisy, message, arg1, arg2); 259 } 260 NoisyLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3)261 public static void NoisyLog(this IEmulationElement e, string message, object arg1, object arg2, object arg3) 262 { 263 LogAs(e, LogLevel.Noisy, message, arg1, arg2, arg3); 264 } 265 Log(this IEmulationElement e, LogLevel type, string message, params object[] args)266 public static void Log(this IEmulationElement e, LogLevel type, string message, params object[] args) 267 { 268 LogAs(e, type, message, args); 269 } 270 Log(this IEmulationElement e, LogLevel type, string message)271 public static void Log(this IEmulationElement e, LogLevel type, string message) 272 { 273 LogAs(e, type, message); 274 } 275 Log(this IEmulationElement e, LogLevel type, string message, object arg1)276 public static void Log(this IEmulationElement e, LogLevel type, string message, object arg1) 277 { 278 LogAs(e, type, message, arg1); 279 } 280 Log(this IEmulationElement e, LogLevel type, string message, object arg1, object arg2)281 public static void Log(this IEmulationElement e, LogLevel type, string message, object arg1, object arg2) 282 { 283 LogAs(e, type, message, arg1, arg2); 284 } 285 Log(this IEmulationElement e, LogLevel type, string message, object arg1, object arg2, object arg3)286 public static void Log(this IEmulationElement e, LogLevel type, string message, object arg1, object arg2, object arg3) 287 { 288 LogAs(e, type, message, arg1, arg2, arg3); 289 } 290 LogAs(object o, LogLevel type, string message, params object[] args)291 public static void LogAs(object o, LogLevel type, string message, params object[] args) 292 { 293 // The inner log method is only skipped if the level of this message is lower than the level set 294 // for any source on any backend. This means that setting any element's log level to Debug will 295 // make all Debug and higher logs get sent to the backends. 296 if(type < minLevel) 297 { 298 return; 299 } 300 var emulationManager = EmulationManager.Instance; 301 if(emulationManager != null) 302 { 303 ((ActualLogger)emulationManager.CurrentEmulation.CurrentLogger).ObjectInnerLog(o, type, message, args); 304 } 305 } 306 LogAs(object o, LogLevel type, string message)307 public static void LogAs(object o, LogLevel type, string message) 308 { 309 if(type < minLevel) 310 { 311 return; 312 } 313 var emulationManager = EmulationManager.Instance; 314 if(emulationManager != null) 315 { 316 ((ActualLogger)emulationManager.CurrentEmulation.CurrentLogger).ObjectInnerLog(o, type, message); 317 } 318 } 319 LogAs(object o, LogLevel type, string message, object arg1)320 public static void LogAs(object o, LogLevel type, string message, object arg1) 321 { 322 if(type < minLevel) 323 { 324 return; 325 } 326 var emulationManager = EmulationManager.Instance; 327 if(emulationManager != null) 328 { 329 ((ActualLogger)emulationManager.CurrentEmulation.CurrentLogger).ObjectInnerLog(o, type, message, arg1); 330 } 331 } 332 LogAs(object o, LogLevel type, string message, object arg1, object arg2)333 public static void LogAs(object o, LogLevel type, string message, object arg1, object arg2) 334 { 335 if(type < minLevel) 336 { 337 return; 338 } 339 var emulationManager = EmulationManager.Instance; 340 if(emulationManager != null) 341 { 342 ((ActualLogger)emulationManager.CurrentEmulation.CurrentLogger).ObjectInnerLog(o, type, message, arg1, arg2); 343 } 344 } 345 LogAs(object o, LogLevel type, string message, object arg1, object arg2, object arg3)346 public static void LogAs(object o, LogLevel type, string message, object arg1, object arg2, object arg3) 347 { 348 if(type < minLevel) 349 { 350 return; 351 } 352 var emulationManager = EmulationManager.Instance; 353 if(emulationManager != null) 354 { 355 ((ActualLogger)emulationManager.CurrentEmulation.CurrentLogger).ObjectInnerLog(o, type, message, arg1, arg2, arg3); 356 } 357 } 358 Flush()359 public static void Flush() 360 { 361 var emulationManager = EmulationManager.Instance; 362 if(emulationManager != null) 363 { 364 ((ActualLogger)emulationManager.CurrentEmulation.CurrentLogger).Flush(); 365 } 366 } 367 368 // see a comment at the top 369 #if !TRACE_ENABLED 370 [Conditional("TRACE_ENABLED")] 371 #endif Trace(this object o, LogLevel type, string message = null, [CallerLineNumber] int lineNumber = 0, [CallerMemberName] string caller = null, [CallerFilePath] string fileName = null)372 public static void Trace(this object o, LogLevel type, string message = null, 373 [CallerLineNumber] int lineNumber = 0, 374 [CallerMemberName] string caller = null, 375 [CallerFilePath] string fileName = null) 376 { 377 var fullMessage = new StringBuilder($"[TRACE][t:{Thread.CurrentThread.Name}/{Thread.CurrentThread.ManagedThreadId}]"); 378 #if DEBUG 379 if(o is IIdentifiable identifiable) 380 { 381 fullMessage.Append($"[s:{identifiable.GetDescription()}]"); 382 } 383 #endif 384 fullMessage.Append($" {message} in {caller} ({Path.GetFileName(fileName)}:{lineNumber})"); 385 386 LogAs(o, type, fullMessage.ToString()); 387 } 388 389 // see a comment at the top 390 #if !TRACE_ENABLED 391 [Conditional("TRACE_ENABLED")] 392 #endif Trace(this object o, string message = null, [CallerLineNumber] int lineNumber = 0, [CallerMemberName] string caller = null, [CallerFilePath] string fileName = null)393 public static void Trace(this object o, string message = null, 394 [CallerLineNumber] int lineNumber = 0, 395 [CallerMemberName] string caller = null, 396 [CallerFilePath] string fileName = null) 397 { 398 Trace(o, LogLevel.Info, message, lineNumber, caller, fileName); 399 } 400 TraceRegion(this object o, string message = null, [CallerLineNumber] int lineNumber = 0, [CallerMemberName] string caller = null, [CallerFilePath] string fileName = null)401 public static IDisposable TraceRegion(this object o, string message = null, 402 [CallerLineNumber] int lineNumber = 0, 403 [CallerMemberName] string caller = null, 404 [CallerFilePath] string fileName = null) 405 { 406 var result = new DisposableWrapper(); 407 Trace(o, $"Entering: {message}", lineNumber, caller, fileName); 408 result.RegisterDisposeAction(() => Trace(o, $"Leaving: {message}. Entered", lineNumber, caller, fileName)); 409 return result; 410 } 411 LogUnhandledRead(this IPeripheral peripheral, long offset)412 public static void LogUnhandledRead(this IPeripheral peripheral, long offset) 413 { 414 peripheral.Log(LogLevel.Warning, "Unhandled read from offset 0x{0:X}.", offset); 415 } 416 LogUnhandledWrite(this IPeripheral peripheral, long offset, ulong value)417 public static void LogUnhandledWrite(this IPeripheral peripheral, long offset, ulong value) 418 { 419 peripheral.Log(LogLevel.Warning, "Unhandled write to offset 0x{0:X}, value 0x{1:X}.", offset, value); 420 } 421 422 public static bool PrintFullName { get; set; } 423 424 public static readonly LogLevel DefaultLogLevel = LogLevel.Info; 425 GetLogger()426 internal static ILogger GetLogger() 427 { 428 var logger = new ActualLogger(); 429 foreach(var backend in backends.Items) 430 { 431 backend.Reset(); 432 } 433 return logger; 434 } 435 436 private static ulong nextEntryId = 0; 437 private static LogLevel minLevel = DefaultLogLevel; 438 private static readonly ConcurrentDictionary<string, ILoggerBackend> backendNames = new ConcurrentDictionary<string, ILoggerBackend>(); 439 private static readonly FastReadConcurrentCollection<ILoggerBackend> backends = new FastReadConcurrentCollection<ILoggerBackend>(); 440 private static readonly ConcurrentDictionary<BackendSourceIdPair, LogLevel> levels = new ConcurrentDictionary<BackendSourceIdPair, LogLevel>(); 441 442 GetGenericName(object o)443 private static string GetGenericName(object o) 444 { 445 if(Misc.IsPythonObject(o)) 446 { 447 return Misc.GetPythonName(o); 448 } 449 var type = o.GetType(); 450 return PrintFullName ? type.FullName : type.Name; 451 } 452 UpdateMinimumLevel()453 private static void UpdateMinimumLevel() 454 { 455 minLevel = levels.Min(l => l.Value); 456 } 457 458 internal class ActualLogger : ILogger 459 { ActualLogger()460 public ActualLogger() 461 { 462 Init(); 463 } 464 Dispose()465 public void Dispose() 466 { 467 if(!SynchronousLogging) 468 { 469 StopLoggingThread(); 470 } 471 } 472 GetMachineName(int id)473 public string GetMachineName(int id) 474 { 475 string objectName; 476 string machineName; 477 if(TryGetName(id, out objectName, out machineName)) 478 { 479 return machineName; 480 } 481 return null; 482 } 483 GetObjectName(int id)484 public string GetObjectName(int id) 485 { 486 string objectName; 487 string machineName; 488 if(TryGetName(id, out objectName, out machineName)) 489 { 490 return objectName; 491 } 492 return null; 493 } 494 TryGetName(int id, out string objectName, out string machineName)495 public bool TryGetName(int id, out string objectName, out string machineName) 496 { 497 object obj; 498 if(logSourcesMap.TryGetObject(id, out obj)) 499 { 500 if(EmulationManager.Instance.CurrentEmulation.TryGetEmulationElementName(obj, out objectName, out machineName)) 501 { 502 return true; 503 } 504 } 505 506 objectName = null; 507 machineName = null; 508 return false; 509 } 510 GetOrCreateSourceId(object source)511 public int GetOrCreateSourceId(object source) 512 { 513 return logSourcesMap.GetOrCreateId(source, () => Interlocked.Increment(ref nextNameId)); 514 } 515 TryGetSourceId(object source, out int id)516 public bool TryGetSourceId(object source, out int id) 517 { 518 return logSourcesMap.TryGetId(source, out id); 519 } 520 ObjectInnerLog(object o, LogLevel type, string message, params object[] args)521 public void ObjectInnerLog(object o, LogLevel type, string message, params object[] args) 522 { 523 int sourceId = (o == null) ? -1 : GetOrCreateSourceId(o); 524 if(args?.Length > 0) 525 { 526 message = string.Format(message, args); 527 } 528 529 var entry = new LogEntry(CustomDateTime.Now, type, message, sourceId, alwaysAppendMachineName, Thread.CurrentThread.ManagedThreadId); 530 531 if(SynchronousLogging) 532 { 533 lock(innerLock) 534 { 535 entry.Id = Logger.nextEntryId++; 536 WriteLogEntryToBackends(entry); 537 } 538 } 539 else 540 { 541 entries.Add(entry); 542 } 543 } 544 Flush()545 public void Flush() 546 { 547 if(SynchronousLogging) 548 { 549 FlushBackends(); 550 return; 551 } 552 553 StopLoggingThread(); 554 555 if(aggregateLogs) 556 { 557 FlushAggregatedLogs(); 558 } 559 560 // switch collections to avoid 561 // stucking forever in the loop below 562 var localEntries = entries; 563 entries = new BlockingCollection<LogEntry>(10000); 564 565 while(localEntries.TryTake(out var entry)) 566 { 567 // we set ids here to avoid the need of locking counter in `ObjectInnerLog` 568 entry.Id = Logger.nextEntryId++; 569 WriteLogEntryToBackends(entry); 570 } 571 572 FlushBackends(); 573 StartLoggingThread(); 574 } 575 576 public bool SynchronousLogging 577 { 578 get => useSynchronousLogging; 579 set 580 { 581 if(useSynchronousLogging == value) 582 { 583 return; 584 } 585 586 useSynchronousLogging = value; 587 if(value) 588 { 589 StopLoggingThread(); 590 } 591 else 592 { 593 StartLoggingThread(); 594 } 595 } 596 } 597 LoggingThreadBody()598 private void LoggingThreadBody() 599 { 600 while(!stopThread) 601 { 602 LogEntry entry; 603 try 604 { 605 entry = entries.Take(cancellationToken.Token); 606 } 607 catch(OperationCanceledException) 608 { 609 break; 610 } 611 612 if(!aggregateLogs) 613 { 614 // we set ids here to avoid the need of locking counter in `ObjectInnerLog` 615 entry.Id = Logger.nextEntryId++; 616 WriteLogEntryToBackends(entry); 617 continue; 618 } 619 620 lock(aggregationFlushLock) 621 { 622 if(entry.EqualsWithoutIdTimeAndCount(lastLoggedEntry)) 623 { 624 repeatLogEntryCount++; 625 if(repeatLogEntryCount >= MaxRepeatedLogs) 626 { 627 FlushAggregatedLogs(); 628 } 629 } 630 else 631 { 632 FlushAggregatedLogs(); 633 634 // we set ids here to avoid the need of locking counter in `ObjectInnerLog` 635 entry.Id = Logger.nextEntryId++; 636 WriteLogEntryToBackends(entry); 637 lastLoggedEntry = entry; 638 } 639 } 640 } 641 } 642 WriteLogEntryToBackends(LogEntry entry)643 private void WriteLogEntryToBackends(LogEntry entry) 644 { 645 var allBackends = Logger.backends.Items; 646 for(var i = 0; i < allBackends.Length; i++) 647 { 648 allBackends[i].Log(entry); 649 } 650 } 651 FlushBackends()652 private void FlushBackends() 653 { 654 var allBackends = Logger.backends.Items; 655 for(var i = 0; i < allBackends.Length; i++) 656 { 657 allBackends[i].Flush(); 658 } 659 } 660 FlushAggregatedLogs()661 private void FlushAggregatedLogs() 662 { 663 if(repeatLogEntryCount == 0) 664 { 665 return; 666 } 667 668 lastLoggedEntry.Count = repeatLogEntryCount; 669 670 WriteLogEntryToBackends(lastLoggedEntry); 671 672 repeatLogEntryCount = 0; 673 674 // reset timer 675 logAggregatorTimer?.Change(MaxAggregateTimeMs, MaxAggregateTimeMs); 676 } 677 678 [PostDeserialization] Init()679 private void Init() 680 { 681 logSourcesMap = new LogSourcesMap(); 682 nextNameId = 0; 683 684 innerLock = new object(); 685 aggregationFlushLock = new object(); 686 687 SynchronousLogging = ConfigurationManager.Instance.Get("general", "use-synchronous-logging", false); 688 alwaysAppendMachineName = ConfigurationManager.Instance.Get("general", "always-log-machine-name", false); 689 aggregateLogs = ConfigurationManager.Instance.Get("general", "collapse-repeated-log-entries", true); 690 691 if(!SynchronousLogging) 692 { 693 entries = new BlockingCollection<LogEntry>(10000); 694 695 StartLoggingThread(); 696 } 697 } 698 StartLoggingThread()699 private void StartLoggingThread() 700 { 701 lock(innerLock) 702 { 703 if(aggregateLogs) 704 { 705 logAggregatorTimer = new Timer(x => 706 { 707 lock(aggregationFlushLock) 708 { 709 FlushAggregatedLogs(); 710 } 711 }, null, MaxAggregateTimeMs, MaxAggregateTimeMs); 712 } 713 714 cancellationToken = new CancellationTokenSource(); 715 loggingThread = new Thread(LoggingThreadBody); 716 loggingThread.IsBackground = true; 717 loggingThread.Name = "Logging thread"; 718 loggingThread.Start(); 719 } 720 } 721 StopLoggingThread()722 private void StopLoggingThread() 723 { 724 lock(innerLock) 725 { 726 if(loggingThread == null) 727 { 728 return; 729 } 730 731 stopThread = true; 732 cancellationToken.Cancel(); 733 loggingThread.Join(); 734 loggingThread = null; 735 736 logAggregatorTimer?.Dispose(); 737 logAggregatorTimer = null; 738 } 739 } 740 741 [Transient] 742 private int repeatLogEntryCount; 743 [Transient] 744 private LogEntry lastLoggedEntry; 745 [Transient] 746 private Timer logAggregatorTimer; 747 [Transient] 748 private bool aggregateLogs; 749 750 [Transient] 751 private bool alwaysAppendMachineName; 752 753 [Transient] 754 private bool useSynchronousLogging; 755 756 [Transient] 757 private object innerLock; 758 759 [Transient] 760 private object aggregationFlushLock; 761 762 [Transient] 763 private Thread loggingThread; 764 765 [Transient] 766 private CancellationTokenSource cancellationToken; 767 768 [Transient] 769 private volatile bool stopThread = false; 770 771 [Transient] 772 private BlockingCollection<LogEntry> entries; 773 [Transient] 774 private int nextNameId; 775 [Transient] 776 private LogSourcesMap logSourcesMap; 777 778 private const int MaxRepeatedLogs = 10000; 779 private const int MaxAggregateTimeMs = 500; 780 781 private class LogSourcesMap 782 { LogSourcesMap()783 public LogSourcesMap() 784 { 785 objectToIdMap = new ConcurrentDictionary<WeakWrapper<object>, int>(); 786 idToObjectMap = new ConcurrentDictionary<int, WeakWrapper<object>>(); 787 } 788 GetOrCreateId(object o, Func<int> idProvider)789 public int GetOrCreateId(object o, Func<int> idProvider) 790 { 791 return objectToIdMap.GetOrAdd(WeakWrapper<object>.CreateForComparison(o), s => 792 { 793 s.ConvertToRealWeakWrapper(); 794 795 var id = idProvider(); 796 idToObjectMap.TryAdd(id, s); 797 return id; 798 }); 799 } 800 TryGetId(object o, out int sourceId)801 public bool TryGetId(object o, out int sourceId) 802 { 803 return objectToIdMap.TryGetValue(WeakWrapper<object>.CreateForComparison(o), out sourceId); 804 } 805 TryGetObject(int id, out object obj)806 public bool TryGetObject(int id, out object obj) 807 { 808 WeakWrapper<object> outResult; 809 var result = idToObjectMap.TryGetValue(id, out outResult); 810 if(result) 811 { 812 return outResult.TryGetTarget(out obj); 813 } 814 815 obj = null; 816 return false; 817 } 818 819 private readonly ConcurrentDictionary<int, WeakWrapper<object>> idToObjectMap; 820 private readonly ConcurrentDictionary<WeakWrapper<object>, int> objectToIdMap; 821 } 822 } 823 824 internal struct BackendSourceIdPair 825 { BackendSourceIdPairAntmicro.Renode.Logging.Logger.BackendSourceIdPair826 public BackendSourceIdPair(ILoggerBackend backend, int sourceId) 827 { 828 this.backend = backend; 829 this.sourceId = sourceId; 830 } 831 832 public readonly ILoggerBackend backend; 833 public readonly int sourceId; 834 } 835 } 836 } 837