|
| 1 | +package kernel |
| 2 | + |
| 3 | +/* |
| 4 | +#include "kernel/bitcoinkernel.h" |
| 5 | +#include <stdlib.h> |
| 6 | +#include <stdint.h> |
| 7 | +
|
| 8 | +// Bridge function: exported Go function that C library can call |
| 9 | +// user_data contains the cgo.Handle ID as void* for callback identification |
| 10 | +extern void go_log_callback_bridge(void* user_data, char* message, size_t message_len); |
| 11 | +
|
| 12 | +// Wrapper function: C helper to create logging connection with Go callback |
| 13 | +// Converts Handle ID to void* and passes to C library |
| 14 | +static inline kernel_LoggingConnection* create_logging_connection_wrapper(uintptr_t context, kernel_LoggingOptions options) { |
| 15 | + return kernel_logging_connection_create((kernel_LogCallback)go_log_callback_bridge, (void*)context, options); |
| 16 | +} |
| 17 | +*/ |
| 18 | +import "C" |
| 19 | +import ( |
| 20 | + "runtime" |
| 21 | + "runtime/cgo" |
| 22 | + "sync" |
| 23 | + "unsafe" |
| 24 | +) |
| 25 | + |
| 26 | +// LogLevel represents the logging level |
| 27 | +type LogLevel int |
| 28 | + |
| 29 | +const ( |
| 30 | + LogLevelTrace LogLevel = iota |
| 31 | + LogLevelDebug |
| 32 | + LogLevelInfo |
| 33 | +) |
| 34 | + |
| 35 | +// LogCategory represents a logging category |
| 36 | +type LogCategory int |
| 37 | + |
| 38 | +const ( |
| 39 | + LogAll LogCategory = iota |
| 40 | + LogBench |
| 41 | + LogBlockStorage |
| 42 | + LogCoinDB |
| 43 | + LogLevelDB |
| 44 | + LogMempool |
| 45 | + LogPrune |
| 46 | + LogRand |
| 47 | + LogReindex |
| 48 | + LogValidation |
| 49 | + LogKernel |
| 50 | +) |
| 51 | + |
| 52 | +// LogCallback is the Go callback function type for log messages. |
| 53 | +type LogCallback func(message string) |
| 54 | + |
| 55 | +// LoggingOptions configures the format of log messages |
| 56 | +type LoggingOptions struct { |
| 57 | + LogTimestamps bool // Prepend a timestamp to log messages |
| 58 | + LogTimeMicros bool // Log timestamps in microsecond precision |
| 59 | + LogThreadNames bool // Prepend the name of the thread to log messages |
| 60 | + LogSourceLocations bool // Prepend the source location to log messages |
| 61 | + AlwaysPrintCategoryLevel bool // Prepend the log category and level to log messages |
| 62 | +} |
| 63 | + |
| 64 | +// LoggingConnection wraps the C kernel_LoggingConnection. |
| 65 | +// Functions changing the logging settings are global and change |
| 66 | +// the settings for all existing kernel_LoggingConnection instances. |
| 67 | +type LoggingConnection struct { |
| 68 | + ptr *C.kernel_LoggingConnection |
| 69 | + handle cgo.Handle // Prevents callback GC until Delete() called |
| 70 | +} |
| 71 | + |
| 72 | +//export go_log_callback_bridge |
| 73 | +func go_log_callback_bridge(user_data unsafe.Pointer, message *C.char, message_len C.size_t) { |
| 74 | + // Convert void* back to Handle - user_data contains Handle ID |
| 75 | + handle := cgo.Handle(user_data) |
| 76 | + // Retrieve original Go callback |
| 77 | + callback := handle.Value().(LogCallback) |
| 78 | + |
| 79 | + goMessage := C.GoStringN(message, C.int(message_len)) |
| 80 | + |
| 81 | + // Call the actual Go callback |
| 82 | + callback(goMessage) |
| 83 | +} |
| 84 | + |
| 85 | +func NewLoggingConnection(callback LogCallback, options LoggingOptions) (*LoggingConnection, error) { |
| 86 | + if callback == nil { |
| 87 | + return nil, ErrInvalidCallback |
| 88 | + } |
| 89 | + |
| 90 | + // Create a handle for the callback - this prevents garbage collection |
| 91 | + // and provides a stable ID that can be passed through C code safely |
| 92 | + handle := cgo.NewHandle(callback) |
| 93 | + |
| 94 | + cOptions := C.kernel_LoggingOptions{ |
| 95 | + log_timestamps: C.bool(options.LogTimestamps), |
| 96 | + log_time_micros: C.bool(options.LogTimeMicros), |
| 97 | + log_threadnames: C.bool(options.LogThreadNames), |
| 98 | + log_sourcelocations: C.bool(options.LogSourceLocations), |
| 99 | + always_print_category_levels: C.bool(options.AlwaysPrintCategoryLevel), |
| 100 | + } |
| 101 | + |
| 102 | + ptr := C.create_logging_connection_wrapper(C.uintptr_t(handle), cOptions) |
| 103 | + if ptr == nil { |
| 104 | + handle.Delete() |
| 105 | + return nil, ErrLoggingConnectionCreation |
| 106 | + } |
| 107 | + |
| 108 | + connection := &LoggingConnection{ |
| 109 | + ptr: ptr, |
| 110 | + handle: handle, |
| 111 | + } |
| 112 | + |
| 113 | + runtime.SetFinalizer(connection, (*LoggingConnection).destroy) |
| 114 | + return connection, nil |
| 115 | +} |
| 116 | + |
| 117 | +func (lc *LoggingConnection) destroy() { |
| 118 | + if lc.ptr != nil { |
| 119 | + C.kernel_logging_connection_destroy(lc.ptr) |
| 120 | + lc.ptr = nil |
| 121 | + } |
| 122 | + if lc.handle != 0 { |
| 123 | + // Delete exposes callback to garbage collection |
| 124 | + lc.handle.Delete() |
| 125 | + lc.handle = 0 |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +func (lc *LoggingConnection) Destroy() { |
| 130 | + runtime.SetFinalizer(lc, nil) |
| 131 | + lc.destroy() |
| 132 | +} |
| 133 | + |
| 134 | +// DisableLogging permanently disables the global internal logger. |
| 135 | +// This function should only be called once and is not thread-safe |
| 136 | +func DisableLogging() { |
| 137 | + C.kernel_disable_logging() |
| 138 | +} |
| 139 | + |
| 140 | +// Global mutex for thread-safe category management |
| 141 | +var loggingMutex = sync.RWMutex{} |
| 142 | + |
| 143 | +// AddLogLevelCategory sets the log level for a specific category or all categories |
| 144 | +func AddLogLevelCategory(category LogCategory, level LogLevel) { |
| 145 | + loggingMutex.Lock() |
| 146 | + defer loggingMutex.Unlock() |
| 147 | + C.kernel_add_log_level_category(C.kernel_LogCategory(category), C.kernel_LogLevel(level)) |
| 148 | +} |
| 149 | + |
| 150 | +// EnableLogCategory enables logging for a specific category or all categories |
| 151 | +func EnableLogCategory(category LogCategory) { |
| 152 | + loggingMutex.Lock() |
| 153 | + defer loggingMutex.Unlock() |
| 154 | + C.kernel_enable_log_category(C.kernel_LogCategory(category)) |
| 155 | +} |
| 156 | + |
| 157 | +// DisableLogCategory disables logging for a specific category or all categories |
| 158 | +func DisableLogCategory(category LogCategory) { |
| 159 | + loggingMutex.Lock() |
| 160 | + defer loggingMutex.Unlock() |
| 161 | + C.kernel_disable_log_category(C.kernel_LogCategory(category)) |
| 162 | +} |
0 commit comments