Utility for handling Unreal Engine format log in Go.
- Parse Unreal Engine log format
- Watch Unreal Engine log file
- Structured log output and handling
Parse UnrealEngine format logs to get the following information.
- Log output time
- Log output frame
- Log category
- Log verbosity
package main
import (
"fmt"
ueloghandler "github.com/y-akahori-ramen/ueLogHandler"
)
func main() {
var log ueloghandler.Log
log = ueloghandler.NewLog("[2022.05.21-13.38.22:383][ 0]LogConfig: Applying CVar settings from Section [/Script/Engine.RendererSettings] File [Engine]")
fmt.Printf("Time:%#v\nFrame:%#v\nCategory:%#v\nVerbosity:%#v\nLog:%#v\n", log.Time, log.Frame, log.Category, log.Verbosity, log.Log)
// Output:
// Time:"2022.05.21-13.38.22:383"
// Frame:" 0"
// Category:"LogConfig"
// Verbosity:""
// Log:"[2022.05.21-13.38.22:383][ 0]LogConfig: Applying CVar settings from Section [/Script/Engine.RendererSettings] File [Engine]"
log = ueloghandler.NewLog("[2022.05.21-15.58.22:810][843]LogWindows: Error: [Callstack] 0x00007ffe5bc37eef UnrealEditor-Core.dll!UnknownFunction []")
fmt.Printf("Time:%#v\nFrame:%#v\nCategory:%#v\nVerbosity:%#v\nLog:%#v\n", log.Time, log.Frame, log.Category, log.Verbosity, log.Log)
// Output:
// Time:"2022.05.21-15.58.22:810"
// Frame:"843"
// Category:"LogWindows"
// Verbosity:"Error"
// Log:"[2022.05.21-15.58.22:810][843]LogWindows: Error: [Callstack] 0x00007ffe5bc37eef UnrealEditor-Core.dll!UnknownFunction []"
}
Watch Unreal Engine log file and handle log for each update.
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
ueloghandler "github.com/y-akahori-ramen/ueLogHandler"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
cancel()
}()
// Create Log handler
logHandler := ueloghandler.NewLogHandler(func(log ueloghandler.Log) error {
// This handler is called when the log file is updated
_, err := fmt.Printf("%#v\n", log)
return err
})
// Create file notifier
pathToLogFile := "ue.log"
watchInterval := time.Millisecond * 500
fileNotifier := ueloghandler.NewFileNotifier(pathToLogFile, watchInterval)
// Start watching log
wacher := ueloghandler.NewWatcher()
wacher.AddLogHandler(logHandler)
err := wacher.Watch(ctx, fileNotifier)
if err != nil {
log.Fatal(err)
}
}
Define structured log format in a schema file, and generate following source code.
- Utility to output structured logs as UnrealEngine logs
- Log handler for structured logs
You can use the generated source code to implement structured log output and handling.
Define the format of the structured log in YAML.
structures:
list:
Sample:
Meta:
Tag: DataTag
Insert: False
Value: 1.23
Value2: 123
Body:
Damage: int32
Name: string
Position: vector3
Sample2:
Meta:
Insert: True
Body:
Count: int32
Build the source code generator and pass the schema file to generate the source code.
cd cmds/structuregen
go build -o gen
./gen -cpp-namespace structuredLog -cpp-out sample.h -go-package main -go-out sample.go -src structure.yaml
When executed, it will output sample.h
and sample.go
.
sample.h
// Code generated by structuregen. DO NOT EDIT.
#pragma once
#include "CoreMinimal.h"
namespace structuredLog {
FString LogSample(int32 Damage,const FString& Name,const FVector& Position)
{
return FString::Printf(TEXT(R"(_BEGIN_STRUCTURED_{"Body":{"Body":{"Damage":%d,"Name":"%s","Position":{"X":%f,"Y":%f,"Z":%f}},"Meta":{"Insert":false,"Tag":"DataTag","Value":1.23,"Value2":123}},"Meta":{"Type":"Sample"}}_END_STRUCTURED_)"),Damage,*Name,Position.X,Position.Y,Position.Z);
}
FString LogSample2(int32 Count)
{
return FString::Printf(TEXT(R"(_BEGIN_STRUCTURED_{"Body":{"Body":{"Count":%d},"Meta":{"Insert":true}},"Meta":{"Type":"Sample2"}}_END_STRUCTURED_)"),Count);
}
}
sample.go
// Code generated by structuregen. DO NOT EDIT.
package main
import ueloghandler "github.com/y-akahori-ramen/ueLogHandler"
type SampleMeta struct {
Tag string
Insert bool
Value float64
Value2 int32
}
type SampleBody struct {
Name string
Position ueloghandler.FVector
Damage int32
}
type SampleData ueloghandler.TStructuredData[SampleMeta, SampleBody]
type SampleHandlerFunc func(SampleData, ueloghandler.Log) error
type SampleLogHandler struct {
f SampleHandlerFunc
}
func (h *SampleLogHandler) Type() string {
return "Sample"
}
func (h *SampleLogHandler) Handle(json string, log ueloghandler.Log) error {
data, err := ueloghandler.JSONToStructuredData[SampleMeta, SampleBody](json)
if err != nil {
return err
}
return h.f(SampleData(data), log)
}
func NewSampleLogHandler(f SampleHandlerFunc) ueloghandler.StructuredLogDataHandler {
return &SampleLogHandler{f: f}
}
type Sample2Meta struct {
Insert bool
}
type Sample2Body struct {
Count int32
}
type Sample2Data ueloghandler.TStructuredData[Sample2Meta, Sample2Body]
type Sample2HandlerFunc func(Sample2Data, ueloghandler.Log) error
type Sample2LogHandler struct {
f Sample2HandlerFunc
}
func (h *Sample2LogHandler) Type() string {
return "Sample2"
}
func (h *Sample2LogHandler) Handle(json string, log ueloghandler.Log) error {
data, err := ueloghandler.JSONToStructuredData[Sample2Meta, Sample2Body](json)
if err != nil {
return err
}
return h.f(Sample2Data(data), log)
}
func NewSample2LogHandler(f Sample2HandlerFunc) ueloghandler.StructuredLogDataHandler {
return &Sample2LogHandler{f: f}
}
The generated sample.h
defines a function that creates a structured log string.
You can use this function to implement structured log output.
#include "sample.h"
void SampleFunc()
{
// Log output using the generated structured log string output function
UE_LOG(LogTemp, Log, TEXT("%s"), *structuredLog::LogSample(10, TEXT("SampleActor"), FVector(1,2,3)));
UE_LOG(LogTemp, Log, TEXT("%s"), *structuredLog::LogSample2(20));
}
The generated sample.go
defines a handler for structured logs.
By registering this handler with the Watcher, you can handle structured logs.
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
ueloghandler "github.com/y-akahori-ramen/ueLogHandler"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
cancel()
}()
// Create Sample structured log handler
sampleLogHandler := NewSampleLogHandler(func(data SampleData, l ueloghandler.Log) error {
_, err := fmt.Printf("Handle SampleLog Meta:%#v Body:%#v\n", data.Meta, data.Body)
return err
})
// Create Sample2 structured log handler
sample2LogHandler := NewSample2LogHandler(func(data Sample2Data, l ueloghandler.Log) error {
_, err := fmt.Printf("Handle Sample2Log Meta:%#v Body:%#v\n", data.Meta, data.Body)
return err
})
// Create structured log handler
structuredLogHandler := ueloghandler.NewStructuredLogHandler()
structuredLogHandler.AddHandler(sampleLogHandler)
structuredLogHandler.AddHandler(sample2LogHandler)
// Create file notifier
pathToLogFile := "ue.log"
watchInterval := time.Millisecond * 500
fileNotifier := ueloghandler.NewFileNotifier(pathToLogFile, watchInterval)
// Start watching log
wacher := ueloghandler.NewWatcher()
wacher.AddLogHandler(structuredLogHandler)
err := wacher.Watch(ctx, fileNotifier)
if err != nil {
log.Fatal(err)
}
}
The format of the schema file is defined in the following CUE file.
schema.cue
Meta always outputs the value specified in the schema file.
Meta is used to inform the Go language structured log handler how to handle logs.
For example, the name of the table to register the structured log in the database.
Body is the value that will be output when UnrealEngine outputs the log.
For example, the location of a damaged character.