Skip to content

Utility for handling Unreal Engine format log in Go.

License

Notifications You must be signed in to change notification settings

y-akahori-ramen/ueLogHandler

Repository files navigation

ueLogHandler

Utility for handling Unreal Engine format log in Go.

日本語版

Feature

  • Parse Unreal Engine log format
  • Watch Unreal Engine log file
  • Structured log output and handling

Parse Unreal Engine log format

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

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)
    }
}

Structured log output and handling

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.

Usage

1.Define structured log format in a schema file

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

2.Generate source code

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}
}

3.Output structured log

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));
}

4.Handle structured log

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)
	}
}

Schema file details

Format

The format of the schema file is defined in the following CUE file.
schema.cue

Meta and Body

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.

About

Utility for handling Unreal Engine format log in Go.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published