Skip to content

Commit a78d204

Browse files
author
BRUNER Patrick
committed
review updates and bugfixes
1 parent 66da11c commit a78d204

5 files changed

Lines changed: 59 additions & 44 deletions

File tree

src/LogExpert.Core/Classes/JsonConverters/ColumnizerJsonConverter.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public override object ReadJson (JsonReader reader, Type objectType, object? exi
9292
try
9393
{
9494
columnizerType = Type.GetType(typeName, throwOnError: false);
95+
//FindColumnizerTypeByAssemblyQualifiedName(typeName);
9596

9697
// If Type.GetType fails, try finding it manually
9798
if (columnizerType == null)
@@ -134,6 +135,7 @@ FileLoadException or
134135
private static Type FindColumnizerTypeByName (string name)
135136
{
136137
// Search all loaded assemblies for a type implementing ILogLineColumnizer with matching GetName()
138+
137139
foreach (var currentAssembly in AppDomain.CurrentDomain.GetAssemblies())
138140
{
139141
foreach (var type in currentAssembly.GetTypes().Where(t => typeof(ILogLineColumnizer).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract))
@@ -181,15 +183,12 @@ private static Type FindColumnizerTypeByAssemblyQualifiedName (string assemblyQu
181183
var typeName = typeNameParts[0].Trim();
182184
var assemblyName = typeNameParts[1].Trim();
183185

184-
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
186+
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == assemblyName))
185187
{
186-
if (assembly.GetName().Name == assemblyName)
188+
var type = assembly.GetType(typeName);
189+
if (type != null && typeof(ILogLineColumnizer).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
187190
{
188-
var type = assembly.GetType(typeName);
189-
if (type != null && typeof(ILogLineColumnizer).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
190-
{
191-
return type;
192-
}
191+
return type;
193192
}
194193
}
195194

src/LogExpert.Core/Helpers/RegexHelper.cs

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using System.Collections.Concurrent;
32
using System.Text.RegularExpressions;
43

@@ -18,7 +17,7 @@ public static class RegexHelper
1817
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(2);
1918

2019
private static readonly ConcurrentDictionary<RegexCacheKey, Regex> _cache = new();
21-
private const int MaxCacheSize = 100;
20+
private const int MAX_CACHE_SIZE = 100;
2221

2322
/// <summary>
2423
/// Creates a regex with timeout protection.
@@ -29,17 +28,11 @@ public static class RegexHelper
2928
/// <returns>A Regex instance with timeout protection.</returns>
3029
/// <exception cref="ArgumentNullException">Thrown if pattern is null.</exception>
3130
/// <exception cref="ArgumentException">Thrown if pattern is invalid.</exception>
32-
public static Regex CreateSafeRegex(
33-
string pattern,
34-
RegexOptions options = RegexOptions.None,
35-
TimeSpan? timeout = null)
31+
public static Regex CreateSafeRegex (string pattern, RegexOptions options = RegexOptions.None, TimeSpan? timeout = null)
3632
{
3733
ArgumentNullException.ThrowIfNull(pattern);
3834

39-
return new Regex(
40-
pattern,
41-
options,
42-
timeout ?? DefaultTimeout);
35+
return new Regex(pattern, options, timeout ?? DefaultTimeout);
4336
}
4437

4538
/// <summary>
@@ -49,16 +42,14 @@ public static Regex CreateSafeRegex(
4942
/// <param name="pattern">The regular expression pattern.</param>
5043
/// <param name="options">Regex options to use.</param>
5144
/// <returns>A cached Regex instance with timeout protection.</returns>
52-
public static Regex GetOrCreateCached(
53-
string pattern,
54-
RegexOptions options = RegexOptions.None)
45+
public static Regex GetOrCreateCached (string pattern, RegexOptions options = RegexOptions.None)
5546
{
5647
var key = new RegexCacheKey(pattern, options);
5748

5849
return _cache.GetOrAdd(key, k =>
5950
{
6051
// Evict oldest entries if cache is full
61-
if (_cache.Count >= MaxCacheSize)
52+
if (_cache.Count >= MAX_CACHE_SIZE)
6253
{
6354
TrimCache();
6455
}
@@ -73,7 +64,7 @@ public static Regex GetOrCreateCached(
7364
/// <param name="pattern">The pattern to validate.</param>
7465
/// <param name="error">Output parameter containing error message if validation fails.</param>
7566
/// <returns>True if the pattern is valid, false otherwise.</returns>
76-
public static bool IsValidPattern(string pattern, out string? error)
67+
public static bool IsValidPattern (string pattern, out string? error)
7768
{
7869
if (string.IsNullOrEmpty(pattern))
7970
{
@@ -103,7 +94,7 @@ public static bool IsValidPattern(string pattern, out string? error)
10394
/// <summary>
10495
/// Clears the regex cache. Useful for testing or memory management.
10596
/// </summary>
106-
public static void ClearCache()
97+
public static void ClearCache ()
10798
{
10899
_cache.Clear();
109100
}
@@ -113,15 +104,15 @@ public static void ClearCache()
113104
/// </summary>
114105
public static int CacheSize => _cache.Count;
115106

116-
private static void TrimCache()
107+
private static void TrimCache ()
117108
{
118109
// Keep most recent 50 entries (half of max)
119-
var toRemove = _cache.Keys.Take(_cache.Count - MaxCacheSize / 2).ToList();
110+
var toRemove = _cache.Keys.Take(_cache.Count - MAX_CACHE_SIZE / 2).ToList();
120111
foreach (var key in toRemove)
121112
{
122-
_cache.TryRemove(key, out _);
113+
_ = _cache.TryRemove(key, out _);
123114
}
124115
}
125116

126-
private record RegexCacheKey(string Pattern, RegexOptions Options);
117+
private record RegexCacheKey (string Pattern, RegexOptions Options);
127118
}

src/PluginRegistry/DefaultPluginLoader.cs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class DefaultPluginLoader : IPluginLoader
1414
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
1515

1616
/// <summary>
17-
/// Loads a plugin from the specified assembly path.
17+
/// Loads all plugins from the specified assembly path.
1818
/// </summary>
1919
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Catch Unexpected errors")]
2020
public PluginLoadResult LoadPlugin (string assemblyPath)
@@ -61,30 +61,45 @@ public PluginLoadResult LoadPlugin (string assemblyPath)
6161

6262
_logger.Debug("Found {Count} plugin type(s) in assembly", pluginTypes.Count);
6363

64-
// Instantiate first plugin type
65-
var pluginType = pluginTypes.First();
66-
_logger.Debug("Instantiating plugin type: {Type}", pluginType.FullName);
64+
// Instantiate ALL plugin types, not just the first one
65+
var plugins = new List<object>();
6766

68-
var plugin = Activator.CreateInstance(pluginType);
67+
foreach (var pluginType in pluginTypes)
68+
{
69+
_logger.Debug("Instantiating plugin type: {Type}", pluginType.FullName);
70+
71+
var plugin = Activator.CreateInstance(pluginType);
72+
73+
if (plugin == null)
74+
{
75+
_logger.Error("Failed to instantiate plugin type: {Type}", pluginType.FullName);
76+
continue; // Skip this plugin but continue with others
77+
}
78+
79+
plugins.Add(plugin);
80+
_logger.Info("Successfully instantiated plugin: {Type}", pluginType.Name);
81+
}
6982

70-
if (plugin == null)
83+
if (plugins.Count == 0)
7184
{
72-
_logger.Error("Failed to instantiate plugin type: {Type}", pluginType.FullName);
7385
return new PluginLoadResult
7486
{
7587
Success = false,
76-
ErrorMessage = $"Failed to create instance of plugin type: {pluginType.FullName}",
88+
ErrorMessage = $"Failed to create instances of any plugin types in assembly",
7789
Manifest = manifest
7890
};
7991
}
8092

81-
_logger.Info("Successfully loaded plugin: {Type}", pluginType.Name);
93+
_logger.Info("Successfully loaded {Count} plugin(s) from assembly", plugins.Count);
8294

95+
// For backward compatibility, return the first plugin instance
96+
// The PluginRegistry will handle loading all types via LoadPluginAssembly
8397
return new PluginLoadResult
8498
{
8599
Success = true,
86-
Plugin = plugin,
87-
Manifest = manifest
100+
Plugin = plugins.First(),
101+
Manifest = manifest,
102+
AllPlugins = plugins
88103
};
89104
}
90105
catch (FileNotFoundException ex)

src/PluginRegistry/Interfaces/IPluginLoader.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,15 @@ public class PluginLoadResult
3535
public bool Success { get; set; }
3636

3737
/// <summary>
38-
/// The loaded plugin instance, if successful.
38+
/// The loaded plugin instance, if successful (for backward compatibility, returns first plugin).
3939
/// </summary>
4040
public object? Plugin { get; set; }
4141

42+
/// <summary>
43+
/// All loaded plugin instances when an assembly contains multiple plugins.
44+
/// </summary>
45+
public List<object>? AllPlugins { get; set; }
46+
4247
/// <summary>
4348
/// The plugin manifest, if available.
4449
/// </summary>

src/RegexColumnizer/RegexColumnizer.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public void Configure (ILogLineColumnizerCallback callback, string configDir)
157157
}
158158
}
159159

160-
string filePath = Path.Combine(configDir, $"{name}.json");
160+
string filePath = Path.Join(configDir, $"{name}Columnizer.json");
161161

162162
RegexColumnizerConfigDialog dlg = new(_config);
163163
if (dlg.ShowDialog() == DialogResult.OK)
@@ -283,8 +283,7 @@ private static void ValidateColumnizerName (string name)
283283
// Check for path traversal patterns
284284
if (name.Contains("..", StringComparison.OrdinalIgnoreCase) || name.Contains('~', StringComparison.OrdinalIgnoreCase))
285285
{
286-
throw new InvalidOperationException(
287-
$"Columnizer name '{name}' contains path traversal patterns which are not allowed");
286+
throw new InvalidOperationException($"Columnizer name '{name}' contains path traversal patterns which are not allowed");
288287
}
289288
}
290289

@@ -329,10 +328,16 @@ public void Init ()
329328
try
330329
{
331330
Regex = RegexHelper.GetOrCreateCached(_config.Expression, RegexOptions.Compiled);
332-
var skip = Regex.GetGroupNames().Length == 1 ? 0 : 1;
331+
var skip = Regex.GetGroupNames().Length == 1
332+
? 0
333+
: 1;
334+
333335
columns = [.. Regex.GetGroupNames().Skip(skip)];
334336
}
335-
catch (Exception ex)
337+
catch (Exception ex) when (ex is ArgumentException or
338+
ArgumentNullException or
339+
OverflowException or
340+
RegexParseException)
336341
{
337342
Regex = null;
338343
columns = ["text"];

0 commit comments

Comments
 (0)