Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement resolving event descriptions from database #14

Merged
merged 9 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions src/EventLogExpert.EventDbTool/CreateDatabaseCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Library.EventProviderDatabase;
using EventLogExpert.Library.Providers;
using System.CommandLine;

namespace EventLogExpert.EventDbTool;

public class CreateDatabaseCommand : DbToolCommand
{
public static Command GetCommand()
{
var createDatabaseCommand = new Command(
name: "create",
description: "Creates a new event database.");
var fileArgument = new Argument<string>(
name: "file",
description: "File to create. Must have a .db extension.");
var filterOption = new Option<string>(
name: "--filter",
description: "Only providers matching specified regex string will be added to the database.");
var skipProvidersInFileOption = new Option<string>(
name: "--skip-providers-in-file",
description: "Any providers found in the specified database file will not be included in the new database. " +
"For example, when creating a database of event providers for Exchange Server, it may be useful " +
"to provide a database of all providers from a fresh OS install with no other products. That way, all the " +
"OS providers are skipped, and only providers added by Exchange or other installed products " +
"would be saved in the new database.");
var verboseOption = new Option<bool>(
name: "--verbose",
description: "Enable verbose logging. May be useful for troubleshooting.");

createDatabaseCommand.AddArgument(fileArgument);
createDatabaseCommand.AddOption(filterOption);
createDatabaseCommand.AddOption(skipProvidersInFileOption);
createDatabaseCommand.AddOption(verboseOption);
createDatabaseCommand.SetHandler((fileOptionValue, filterOptionValue, verboseOptionValue, skipProvidersInFileOption) =>
{
CreateDatabase(fileOptionValue, filterOptionValue, verboseOptionValue, skipProvidersInFileOption);
},
fileArgument, filterOption, verboseOption, skipProvidersInFileOption);

return createDatabaseCommand;
}

public static void CreateDatabase(string path, string filter, bool verboseLogging, string skipProvidersInFile)
{
if (File.Exists(path))
{
Console.WriteLine($"Cannot create database because file already exists: {path}");
return;
}

if (Path.GetExtension(path) != ".db")
{
Console.WriteLine("File extension must be .db.");
return;
}

var skipProviderNames = new HashSet<string>();

if (skipProvidersInFile != null)
{
if (!File.Exists(skipProvidersInFile))
{
Console.WriteLine($"File not found: {skipProvidersInFile}");
}

using var skipDbContext = new EventProviderDbContext(skipProvidersInFile, readOnly: true);
foreach (var provider in skipDbContext.ProviderDetails)
{
skipProviderNames.Add(provider.ProviderName);
}

Console.WriteLine($"Found {skipProviderNames.Count} providers in file {skipProvidersInFile}. " +
"These will not be included in the new database.");
}

var providerNames = GetLocalProviderNames(filter);
if (!providerNames.Any())
{
Console.WriteLine($"No providers found matching filter {filter}.");
return;
}

var providerNamesNotSkipped = providerNames.Where(name => !skipProviderNames.Contains(name)).ToList();

var numberSkipped = providerNames.Count - providerNamesNotSkipped.Count;
if (numberSkipped > 0)
{
Console.WriteLine($"{numberSkipped} providers were skipped due to being present in the specified database.");
}

using var dbContext = new EventProviderDbContext(path, readOnly: false);

LogProviderDetailHeader(providerNamesNotSkipped);

foreach (var providerName in providerNamesNotSkipped)
{
var provider = new EventMessageProvider(providerName, verboseLogging ? s => Console.WriteLine(s) : s => { });
var details = provider.LoadProviderDetails();
if (details != null)
{
dbContext.ProviderDetails.Add(details);

LogProviderDetails(details);

details = null;
}
}

Console.WriteLine();
Console.WriteLine("Saving database. Please wait...");

dbContext.SaveChanges();

Console.WriteLine("Done!");
}
}
47 changes: 47 additions & 0 deletions src/EventLogExpert.EventDbTool/DbToolCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Library.Providers;
using System.Diagnostics.Eventing.Reader;
using System.Text.RegularExpressions;

namespace EventLogExpert.EventDbTool;

public class DbToolCommand
{
private static string _providerDetailFormat = "{0, -14} {1, 8} {2, 8} {3, 8} {4, 8} {5, 8}";

public static List<string> GetLocalProviderNames(string filter)
{
var session = new EventLogSession();
var providers = new List<string>(session.GetProviderNames().Distinct().OrderBy(name => name));

if (!string.IsNullOrEmpty(filter))
{
var regex = new Regex(filter, RegexOptions.IgnoreCase);
providers = providers.Where(p => regex.IsMatch(p)).ToList();
}

return providers;
}

public static void LogProviderDetailHeader(IEnumerable<string> providerNames)
{
var maxNameLength = providerNames.Any() ? providerNames.Max(p => p.Length) : 14;
if (maxNameLength < 14) maxNameLength = 14;
_providerDetailFormat = "{0, -" + maxNameLength + "} {1, 8} {2, 8} {3, 8} {4, 8} {5, 8}";
Console.WriteLine(string.Format(_providerDetailFormat, "Provider Name", "Events", "Keywords", "Opcodes", "Tasks", "Messages"));
}

public static void LogProviderDetails(ProviderDetails details)
{
Console.WriteLine(string.Format(
_providerDetailFormat,
details.ProviderName,
details.Events.Count,
details.Keywords.Count,
details.Opcodes.Count,
details.Tasks.Count,
details.Messages.Count));
}
}
116 changes: 116 additions & 0 deletions src/EventLogExpert.EventDbTool/DiffDatabaseCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Library.EventProviderDatabase;
using EventLogExpert.Library.Providers;
using System.CommandLine;

namespace EventLogExpert.EventDbTool;

public class DiffDatabaseCommand : DbToolCommand
{
public static Command GetCommand()
{
var diffDatababaseCommand = new Command(
name: "diff",
description: "Given two databases, produces a third database containing all providers " +
"from the second database which are not in the first database.");
var dbOneArgument = new Argument<string>(
name: "first db",
description: "The first database to compare.");
var dbTwoArgument = new Argument<string>(
name: "second db",
description: "The second database to compare.");
var newDbArgument = new Argument<string>(
name: "new db",
description: "The new database containing only the providers in the second db which are not in the first db. Must have a .db extension.");
var verboseOption = new Option<bool>(
name: "--verbose",
description: "Verbose logging. May be useful for troubleshooting.");
diffDatababaseCommand.AddArgument(dbOneArgument);
diffDatababaseCommand.AddArgument(dbTwoArgument);
diffDatababaseCommand.AddArgument(newDbArgument);
diffDatababaseCommand.AddOption(verboseOption);
diffDatababaseCommand.SetHandler((dbOneArgValue, dbTwoArgValue, newDbArgValue, verboseOptionValue) =>
{
DiffDatabase(dbOneArgValue, dbTwoArgValue, newDbArgValue, verboseOptionValue);
},
dbOneArgument, dbTwoArgument, newDbArgument, verboseOption);

return diffDatababaseCommand;
}

public static void DiffDatabase(string dbOne, string dbTwo, string newDb, bool verbose)
{
foreach (var path in new[] { dbOne, dbTwo })
{
if (!File.Exists(path))
{
Console.WriteLine($"File not found: {path}");
return;
}
}

if (File.Exists(newDb))
{
Console.WriteLine($"File already exists: {newDb}");
return;
}

if (Path.GetExtension(newDb) != ".db")
{
Console.WriteLine($"New db path must have a .db extension.");
return;
}

var dbOneProviderNames = new HashSet<string>();

using (var dbOneContext = new EventProviderDbContext(dbOne, readOnly: true))
{
dbOneContext.ProviderDetails.Select(p => p.ProviderName).ToList().ForEach(name => dbOneProviderNames.Add(name));
}

var providersCopied = new List<ProviderDetails>();

using var dbTwoContext = new EventProviderDbContext(dbTwo, readOnly: true);
using var newDbContext = new EventProviderDbContext(newDb, readOnly: false);

foreach (var details in dbTwoContext.ProviderDetails)
{
if (dbOneProviderNames.Contains(details.ProviderName))
{
if (verbose) Console.WriteLine($"Skipping {details.ProviderName} because it is present in both databases.");
continue;
}
else
{
if (verbose) Console.WriteLine($"Copying {details.ProviderName} because it is present in second db but not first db.");

newDbContext.ProviderDetails.Add(new ProviderDetails
{
ProviderName = details.ProviderName,
Events = details.Events,
Keywords = details.Keywords,
Messages = details.Messages,
Opcodes = details.Opcodes,
Tasks = details.Tasks
});

providersCopied.Add(details);
}
}

newDbContext.SaveChanges();

if (providersCopied.Count > 0)
{
Console.WriteLine("Providers copied to new database:");
Console.WriteLine();
LogProviderDetailHeader(providersCopied.Select(p => p.ProviderName));
foreach (var provider in providersCopied)
{
LogProviderDetails(provider);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<AssemblyName>eventdbtool</AssemblyName>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\EventLogExpert.Library\EventLogExpert.Library.csproj" />
</ItemGroup>

</Project>
Loading