Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
bnbdr committed Jan 17, 2020
1 parent 1e75b24 commit a4e48df
Show file tree
Hide file tree
Showing 14 changed files with 1,671 additions and 0 deletions.
469 changes: 469 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# TraceLogging for Python
This small package implements a `TraceLoggingProvider` for publishing ETW events in **Windows**.


**Use at your own risk.**

# Requirements
- python 3.8.x

# Installation
```bash
pip install tracelogging
```

# Usage
All usage examples are assuming an enabled EventTrace session for the provider(you can use [EtwConsumer](./SampleEtwConsumer/))).

TheFollowing examples are taken from [here](./examples/)

## Basic logging for the extremely lazy
Similar usage to python's own logging. No handlers, no formatters, no `exception` method, no format or additional arguments supported.


The name given to a provider will be used to generate the appropriate GUID the same way `TraceEvent` does.
```py
import tracelogging
log = tracelogging.getLogger('MyLoggerName')

log.debug('ging')
log.info('rmation')
log.warning('be careful')
log.error('err')
log.critical('oh no!')
```


## Defining your very own provider
Defines a provider named `PythonProvider` that can publish an event named `BasicEvent` without any additional data
```py
from tracelogging import Provider, event

class PythonProvider(Provider):
@event() # mind the parentheses
def BasicEvent(self):
pass

log = PythonProvider()
log.BasicEvent()
```

## Advanced usage
You can override the provider's `Name` directly by setting the `Name` class member to whatever you wish.
Same can be done with the `Guid` member, by setting it to an instance of `UUID` with the desired value.


You can set values for the event's descriptor using the `event` decoraotr, just like you would with `TraceEvent` ([or EVENT_DESCRIPTOR struct](https://docs.microsoft.com/en-us/windows/win32/api/evntprov/ns-evntprov-event_descriptor)). You may also specify an override to the event name.

In order to add data to the event, you must use python's type-hinting with the supported types(see Types)
```py
from tracelogging import Provider, event, Types, TraceLevel

class PythonProvider(Provider):
Name = 'Company-Product-Component'

@event(Name='FileSize', Id=1, Level=TraceLevel.Warning, Keyword=0x01)
def not_a_nice_event_name(self, file_path:Types.UnicodeString, file_size:Types.UInt32):
print('this will be called after the event is written, if you wish to implement anything here')

log = PythonProvider()
log.not_a_nice_event_name('C:\\windows\\system32\\calc.exe', 0x1000) # will send event named 'FileSize'
```

## Current supported types
Type | Python | Description
--|--|--
`UnicodeString` | `str` | null terminated UTF-16 string
`CountedUnicodeString` | `str` | size prefix (WORD) followed by UTF-16 string (not terminated)
`UInt32` | `int` | unsigned DWORD
`Int32` | `int` | signed DWORD
`UInt64` | `int` | unsigned QWORD
`Int64` | `int` | signed QWORD

# Notes
- Read contents of `TraceLoggingProvider.h` header for more info
- Most logic is performed during provider class definition. Instantiation only registers and 'sets' the provider as one capable of sending self-described events
- Providers can inherit events from base classes, though doing so is discouraged.
- Internal code uses a sort of singleton to prevent multiple instances of the same provider

# License
MIT

# Troubleshooting
set environment variable `TLG_LOG` to the desired log level (1 for high verbosity, 0 to turn off)
25 changes: 25 additions & 0 deletions SampleEtwConsumer/EtwConsumer.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2000
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EtwConsumer", "EtwConsumer\EtwConsumer.csproj", "{EE0AB704-AFE5-40DB-9F65-2C25BBADF24A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EE0AB704-AFE5-40DB-9F65-2C25BBADF24A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE0AB704-AFE5-40DB-9F65-2C25BBADF24A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE0AB704-AFE5-40DB-9F65-2C25BBADF24A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE0AB704-AFE5-40DB-9F65-2C25BBADF24A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C38F90C1-423F-4591-B00B-6E367EC9BAD9}
EndGlobalSection
EndGlobal
6 changes: 6 additions & 0 deletions SampleEtwConsumer/EtwConsumer/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>
79 changes: 79 additions & 0 deletions SampleEtwConsumer/EtwConsumer/EtwConsumer.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.49\build\Microsoft.Diagnostics.Tracing.TraceEvent.props" Condition="Exists('..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.49\build\Microsoft.Diagnostics.Tracing.TraceEvent.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{EE0AB704-AFE5-40DB-9F65-2C25BBADF24A}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>EtwConsumer</RootNamespace>
<AssemblyName>EtwConsumer</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Dia2Lib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.49\lib\net45\Dia2Lib.dll</HintPath>
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="Microsoft.Diagnostics.FastSerialization, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.49\lib\net45\Microsoft.Diagnostics.FastSerialization.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Diagnostics.Tracing.TraceEvent, Version=2.0.49.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.49\lib\net45\Microsoft.Diagnostics.Tracing.TraceEvent.dll</HintPath>
</Reference>
<Reference Include="OSExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.49\lib\net45\OSExtensions.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="TraceReloggerLib, Version=0.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.49\lib\net45\TraceReloggerLib.dll</HintPath>
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.49\build\Microsoft.Diagnostics.Tracing.TraceEvent.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.2.0.49\build\Microsoft.Diagnostics.Tracing.TraceEvent.props'))" />
</Target>
</Project>
51 changes: 51 additions & 0 deletions SampleEtwConsumer/EtwConsumer/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Session;
using System;
using System.Text;
using System.Threading.Tasks;

namespace EtwConsumer
{
class Program
{
static void Main(string[] providerNames)
{
var sessionName = "SampleEtwConsumerSession";

Console.WriteLine($"Creating session: '{sessionName}'");
using (var session = new TraceEventSession(sessionName))
{
Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
{
Console.WriteLine("stopping");
session.Dispose();
};

session.Source.Dynamic.AddCallbackForProviderEvents((providerName, eventName) =>
{
return EventFilterResponse.AcceptEvent;
}, eventData =>
{
Console.WriteLine(eventData.ToString());
});

foreach (var name in providerNames)
{
if (name.StartsWith("{") && name.EndsWith("}"))
{
var guid = new Guid(name);
session.EnableProvider(guid, TraceEventLevel.Verbose);
Console.WriteLine($"enabled {guid}");
}
else
{
session.EnableProvider(name, TraceEventLevel.Verbose);
Console.WriteLine($"enabled {name}");
}
}

session.Source.Process();
}
}
}
}
36 changes: 36 additions & 0 deletions SampleEtwConsumer/EtwConsumer/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("EtwConsumer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("EtwConsumer")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ee0ab704-afe5-40db-9f65-2c25bbadf24a")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
4 changes: 4 additions & 0 deletions SampleEtwConsumer/EtwConsumer/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Diagnostics.Tracing.TraceEvent" version="2.0.49" targetFramework="net461" />
</packages>
10 changes: 10 additions & 0 deletions examples/ex1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import tracelogging

log = tracelogging.getLogger('MyLoggerName')

# run: EtwConsumer.exe MyLoggerName
log.debug('ging')
log.info('rmation')
log.warning('be careful')
log.error('err')
log.critical('oh no!')
11 changes: 11 additions & 0 deletions examples/ex2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from tracelogging import Provider, event

class PythonProvider(Provider):
@event() # mind the parentheses
def BasicEvent(self):
pass

log = PythonProvider()

# run: EtwConsumer.exe PythonProvider
log.BasicEvent()
12 changes: 12 additions & 0 deletions examples/ex3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from tracelogging import Provider, event, Types, TraceLevel

class PythonProvider(Provider):
Name = 'Company-Product-Component'

@event(Name='FileSize', Id=1, Level=TraceLevel.Warning, Keyword=0x01)
def not_a_nice_event_name(self, file_path:Types.UnicodeString, file_size:Types.UInt32):
print('this will be called after the event is written, if you wish to implement anything here')

# run: EtwConsumer.exe Company-Product-Component
log = PythonProvider()
log.not_a_nice_event_name('C:\\windows\\system32\\calc.exe', 0x1000) # will send event named 'FileSize'
24 changes: 24 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from setuptools import setup
import tracelogging


author = 'bnbdr'
setup(
name='tracelogging',
version='.'.join(map(str, tracelogging.version)),
author=author,
author_email='bad.32@outlook.com',
url='https://github.com/{}/tracelogging'.format(author),
description="python tracelogging for windows",
long_description=tracelogging.__doc__,
long_description_content_type="text/markdown",
license='MIT',
keywords='windows tracelogging python etw trace',
py_modules=['tracelogging'],
classifiers=[
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: Microsoft :: Windows",
],
)

0 comments on commit a4e48df

Please sign in to comment.