A comprehensive, production-ready Go library for working with Windows Event Logs. This library provides a clean, idiomatic Go interface to the Windows Event Log API (wevtapi.dll).
- ✅ Subscribe to real-time events - Monitor event logs as events occur
- ✅ Query historical events - Search and filter past events with XPath queries
- ✅ Channel management - List, clear, and export event log channels
- ✅ Bookmark support - Resume reading from specific positions
- ✅ Query builder - Construct complex XPath queries easily
- ✅ Full event details - Access all event properties (XML, structured data)
- ✅ Sysmon support - Comprehensive support for Sysmon events with specialized parsing
- ✅ Cross-platform stubs - Compile on non-Windows platforms (returns errors)
- ✅ Production-ready - Proper error handling and resource cleanup
go get github.com/Ricemug/wineventlog- Windows operating system
- Go 1.21 or later
- Administrator privileges may be required for certain operations (e.g., Security log)
package main
import (
"fmt"
"log"
"github.com/Ricemug/wineventlog"
)
func main() {
// Create a reader for the System channel
reader, err := wineventlog.NewChannelReader("System")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Read last 10 events
events, err := reader.ReadBatch(10)
if err != nil {
log.Fatal(err)
}
for _, event := range events {
fmt.Printf("EventID: %d | Time: %s | Provider: %s\n",
event.EventID,
event.TimeCreated,
event.ProviderName,
)
event.Close()
}
}package main
import (
"fmt"
"log"
"os"
"os/signal"
"github.com/Ricemug/wineventlog"
)
func main() {
// Create subscriber
subscriber := wineventlog.NewSubscriber("System",
func(event *wineventlog.Event) error {
fmt.Printf("New event: %d from %s\n",
event.EventID,
event.ProviderName,
)
return nil
},
)
if err := subscriber.Start(); err != nil {
log.Fatal(err)
}
defer subscriber.Stop()
// Wait for Ctrl+C
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
<-sigChan
}// Build a query
query := wineventlog.NewQueryBuilder().
EventID(1074). // Specific event ID
Level(2). // Error level
Provider("Microsoft-Windows-System").
Build()
// Create reader with query
reader, err := wineventlog.NewChannelReader("System",
wineventlog.WithQueryString(query),
)
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Read matching events
events, err := reader.ReadBatch(100)
// ... process eventsRepresents a Windows event log event.
type Event struct {
ProviderName string // Event provider
EventID uint16 // Event ID
Level uint8 // Event level (1-5)
TimeCreated time.Time // Event timestamp
EventRecordID uint64 // Unique record ID
Channel string // Channel name
Computer string // Computer name
XML string // Full event XML
// ... more fields
}Methods:
Close() error- Close the event handleGetEventData() (map[string]string, error)- Extract event data as key-value pairsString() string- Human-readable representation
Subscribe to real-time events from a channel.
subscriber := wineventlog.NewSubscriber(channel, callback, opts...)
err := subscriber.Start()
// ... events are delivered to callback
subscriber.Stop()Options:
WithQuery(query string)- Filter events with XPath query
Query historical events from a channel.
query, err := wineventlog.NewQuery(channel, opts...)
event, err := query.Next(timeout)
events, err := query.GetEvents(maxEvents, timeout)
query.Close()Methods:
Next(timeout uint32) (*Event, error)- Get next eventGetEvents(maxEvents int, timeout uint32) ([]*Event, error)- Get batch of eventsSeek(position int64, flags uint32) error- Seek to positionSeekToFirst() error- Seek to first eventSeekToLast() error- Seek to last eventClose() error- Close query handle
Build XPath queries easily.
query := wineventlog.NewQueryBuilder().
EventID(1000). // Single event ID
EventIDRange(1000, 2000). // Event ID range
Level(2). // Error level
Provider("ProviderName"). // Provider filter
TimeRange(startTime, endTime). // Time range
Build()Simplified interface for reading events.
reader, err := wineventlog.NewChannelReader(channel, opts...)
event, err := reader.Read()
events, err := reader.ReadBatch(maxEvents)
reader.Close()channels, err := wineventlog.ListChannels()
for _, channel := range channels {
fmt.Println(channel)
}// Clear with backup
err := wineventlog.ClearChannel("System", "backup.evtx")
// Clear without backup
err := wineventlog.ClearChannel("System")// Export all events
err := wineventlog.ExportChannel("System", "export.evtx")
// Export with query
query := "*[System[Level=2]]" // Errors only
err := wineventlog.ExportChannel("System", "errors.evtx", query)// Read all events from a channel
events, err := wineventlog.ReadAllEvents("Application")
// Read with query
events, err := wineventlog.ReadAllEvents("System",
"*[System[EventID=1074]]")// Count all events
count, err := wineventlog.CountEvents("System")
// Count with query
count, err := wineventlog.CountEvents("System",
"*[System[Level=2]]")Bookmarks allow you to save your position in an event log and resume later.
// Create bookmark
bookmark, err := wineventlog.NewBookmark()
defer bookmark.Close()
// Update bookmark with event
err = bookmark.Update(event)
// Get bookmark XML (save this)
xml, err := bookmark.GetXML()
// Later, restore bookmark
bookmark, err = wineventlog.NewBookmark(xml)1- Critical2- Error3- Warning4- Information5- Verbose
System- System eventsApplication- Application eventsSecurity- Security audit events (requires admin)Setup- Setup eventsMicrosoft-Windows-PowerShell/Operational- PowerShell eventsMicrosoft-Windows-Sysmon/Operational- Sysmon events (if installed)- Many more... (use
ListChannels()to discover)
This library includes comprehensive support for Microsoft Sysmon events. See SYSMON.md for detailed documentation.
Monitor process creation:
if !wineventlog.IsSysmonInstalled() {
log.Fatal("Sysmon is not installed")
}
query := wineventlog.NewSysmonQuery().ProcessCreate().Build()
subscriber := wineventlog.NewSysmonSubscriber(
func(event *wineventlog.SysmonEvent) error {
fmt.Printf("New Process: %s (PID: %d)\n",
event.Image, event.ProcessId)
fmt.Printf(" Command: %s\n", event.CommandLine)
fmt.Printf(" Parent: %s\n", event.ParentImage)
return nil
},
wineventlog.WithQuery(query),
)
subscriber.Start()
defer subscriber.Stop()Monitor network connections:
query := wineventlog.NewSysmonQuery().NetworkConnect().Build()
subscriber := wineventlog.NewSysmonSubscriber(
func(event *wineventlog.SysmonEvent) error {
fmt.Printf("Connection: %s:%d → %s:%d\n",
event.SourceIp, event.SourcePort,
event.DestinationIp, event.DestinationPort)
return nil
},
wineventlog.WithQuery(query),
)
subscriber.Start()
defer subscriber.Stop()See SYSMON.md for comprehensive Sysmon documentation and examples.
// All events
"*"
// Specific event ID
"*[System[EventID=1074]]"
// Event ID range
"*[System[EventID>=1000 and EventID<=2000]]"
// Error level only
"*[System[Level=2]]"
// Multiple conditions
"*[System[EventID=1074 and Level=4]]"
// Provider filter
"*[System[Provider[@Name='Microsoft-Windows-Kernel-Power']]]"
// Time range
"*[System[TimeCreated[@SystemTime>='2024-01-01T00:00:00' and @SystemTime<='2024-12-31T23:59:59']]]"
// Complex query
"*[System[(EventID=1074 or EventID=6006) and Level<=3]]"All functions return errors following Go conventions:
reader, err := wineventlog.NewChannelReader("NonExistent")
if err != nil {
// Handle error (channel doesn't exist, permission denied, etc.)
log.Printf("Failed to create reader: %v", err)
return
}
defer reader.Close()Common errors:
ERROR_ACCESS_DENIED- Insufficient permissions (try running as admin)ERROR_EVT_CHANNEL_NOT_FOUND- Channel doesn't existERROR_NO_MORE_ITEMS- No more events to readERROR_INSUFFICIENT_BUFFER- Internal buffer issue (usually handled automatically)
Always close resources when done:
// Events
event, err := reader.Read()
// ... use event
event.Close() // Important!
// Queries and readers
query, err := wineventlog.NewQuery(...)
defer query.Close()
// Subscribers
subscriber := wineventlog.NewSubscriber(...)
defer subscriber.Stop()
// Bookmarks
bookmark, err := wineventlog.NewBookmark()
defer bookmark.Close()- Batch reads - Use
ReadBatch()instead of multipleRead()calls - Close events - Always close events to free memory
- Use queries - Filter at the API level rather than in Go
- Seek efficiently - Use
SeekToLast()for recent events - Limit results - Don't read entire logs at once for large logs
See the examples directory for complete working examples:
- List all channels
- Read events from a channel
- Subscribe to real-time events
- Query with filters
- Export events to file
- Use bookmarks
- Count events
- Subscribe with custom query
The library compiles on non-Windows platforms but returns errNotSupported errors. This allows you to write cross-platform code:
events, err := wineventlog.ReadAllEvents("System")
if err != nil {
if err.Error() == "wineventlog: not supported on this platform" {
// Handle non-Windows platform
return
}
// Handle actual error
}# Run tests on Windows
go test ./...
# Run with verbose output
go test -v ./...Note: Some tests require administrator privileges.
MIT License - see LICENSE file for details
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
For issues, questions, or contributions:
- GitHub Issues: https://github.com/Ricemug/wineventlog/issues
- Documentation: https://pkg.go.dev/github.com/Ricemug/wineventlog
If you find this project helpful, consider supporting its development:
- Full Windows Event Log API support
- Subscribe to real-time events
- Query historical events
- Channel management
- Bookmark support
- Query builder
- Cross-platform stubs
- Comprehensive examples and documentation