From 83cdf8c11c5713fd383e50e712763959b2e3e87c Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Thu, 31 Jul 2025 10:53:34 -0400 Subject: [PATCH] Load data from the OSLogStore --- src/oslog_table.mm | 190 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 159 insertions(+), 31 deletions(-) diff --git a/src/oslog_table.mm b/src/oslog_table.mm index 3a37e31..ef3a851 100644 --- a/src/oslog_table.mm +++ b/src/oslog_table.mm @@ -1,21 +1,144 @@ #import #import +#include #include #include -#include - namespace duckdb { namespace oslog { namespace { +class Column { + public: + Column(idx_t index, const char* name) : index_(index), name_(name) {} + virtual ~Column() = default; + + virtual LogicalType type() const = 0; + virtual Value Produce(OSLogEntry*) const = 0; + + idx_t index() const { return index_; } + const string& name() const { return name_; } + + private: + const idx_t index_; + const string name_; +}; + +class DateColumn : public Column { + public: + DateColumn(idx_t i) : Column(i, "date") {} + LogicalType type() const override { return LogicalType::TIMESTAMP_MS; } + // TODO: get nsecs? + Value Produce(OSLogEntry* log) const override { + NSTimeInterval ti = [static_cast(log) date].timeIntervalSince1970; + double intg; + double frac = std::modf(ti, &intg); + int64_t millis = intg; + millis *= 1000; + millis += static_cast(frac) * 1000; + return Value::TIMESTAMPMS(timestamp_ms_t(millis)); + } +}; + +class LevelColumn : public Column { + public: + LevelColumn(idx_t i) : Column(i, "level") {} + LogicalType type() const override { return LogicalType::VARCHAR; } + Value Produce(OSLogEntry* log) const override { + OSLogEntryLogLevel level = [static_cast(log) level]; + switch (level) { + case OSLogEntryLogLevelUndefined: return Value("undefined"); + case OSLogEntryLogLevelDebug: return Value("debug"); + case OSLogEntryLogLevelInfo: return Value("info"); + case OSLogEntryLogLevelNotice: return Value("notice"); + case OSLogEntryLogLevelError: return Value("error"); + case OSLogEntryLogLevelFault: return Value("fault"); + } + } +}; + +class SubsystemColumn : public Column { + public: + SubsystemColumn(idx_t i) : Column(i, "subsystem") {} + LogicalType type() const override { return LogicalType::VARCHAR; } + Value Produce(OSLogEntry* log) const override { return Value([static_cast(log) subsystem].UTF8String); } +}; + +class CategoryColumn : public Column { + public: + CategoryColumn(idx_t i) : Column(i, "category") {} + LogicalType type() const override { return LogicalType::VARCHAR; } + Value Produce(OSLogEntry* log) const override { return Value([static_cast(log) category].UTF8String); } +}; + +class ProcessColumn : public Column { + public: + ProcessColumn(idx_t i) : Column(i, "process") {} + LogicalType type() const override { return LogicalType::VARCHAR; } + Value Produce(OSLogEntry* log) const override { return Value([static_cast(log) process].UTF8String); } +}; + +class ProcessIdColumn : public Column { + public: + ProcessIdColumn(idx_t i) : Column(i, "processId") {} + LogicalType type() const override { return LogicalType::BIGINT; } + Value Produce(OSLogEntry* log) const override { return Value([static_cast(log) processIdentifier]); } +}; + +class SenderColumn : public Column { + public: + SenderColumn(idx_t i) : Column(i, "sender") {} + LogicalType type() const override { return LogicalType::VARCHAR; } + Value Produce(OSLogEntry* log) const override { return Value([static_cast(log) sender].UTF8String); } +}; + +class ThreadIdColumn : public Column { + public: + ThreadIdColumn(idx_t i) : Column(i, "threadId") {} + LogicalType type() const override { return LogicalType::UBIGINT; } + Value Produce(OSLogEntry* log) const override { return Value::UBIGINT([static_cast(log) threadIdentifier]); } +}; + +class MessageColumn : public Column { + public: + MessageColumn(idx_t i) : Column(i, "message") {} + LogicalType type() const override { return LogicalType::VARCHAR; } + Value Produce(OSLogEntry* log) const override { return Value([static_cast(log) composedMessage].UTF8String); } +}; + +class FormatStringColumn : public Column { + public: + FormatStringColumn(idx_t i) : Column(i, "formatString") {} + LogicalType type() const override { return LogicalType::VARCHAR; } + Value Produce(OSLogEntry* log) const override { return Value([static_cast(log) formatString].UTF8String); } +}; + +const vector& GetColumns() { + idx_t i = 0; + // XXX - don't leak this + static const vector kColumns = { + new DateColumn(i++), + new LevelColumn(i++), + new SubsystemColumn(i++), + new CategoryColumn(i++), + new ProcessColumn(i++), + new ProcessIdColumn(i++), + new SenderColumn(i++), + new ThreadIdColumn(i++), + new MessageColumn(i++), + new FormatStringColumn(i++), + }; + return kColumns; +} + struct OSLogLocalState : public LocalTableFunctionState { }; -struct OSLogTableFunction : public TableFunctionData { +struct OSLogTableFunctionData : public TableFunctionData { OSLogStore* store = nil; + OSLogEnumerator* enumerator = nil; }; NSString* NSStringFromString(const string& str) { @@ -31,7 +154,7 @@ unique_ptr OSLogBind(ClientContext& context, throw InvalidInputException("Missing 'archive' parameter"); } - auto data = make_uniq(); + auto data = make_uniq(); NSError* error = nil; NSURL* archive_url = [NSURL fileURLWithPath:NSStringFromString(StringValue::Get(archive))]; @@ -40,32 +163,10 @@ unique_ptr OSLogBind(ClientContext& context, throw IOException(error.description.UTF8String); } - names = { - "date", - "eventType", - "level", - "subsystem", - "category", - "process", - "processId", - "sender", - "threadId", - "message", - "formatString", - }; - return_types = { - LogicalType::TIMESTAMP, // date - LogicalType::VARCHAR, // eventType - LogicalType::VARCHAR, // level - LogicalType::VARCHAR, // subsystem - LogicalType::VARCHAR, // category - LogicalType::VARCHAR, // process - LogicalType::BIGINT, // processId - LogicalType::VARCHAR, // sender - LogicalType::BIGINT, // threadId - LogicalType::VARCHAR, // message - LogicalType::VARCHAR, // formatString - }; + for (const auto* column : GetColumns()) { + names.push_back(column->name()); + return_types.push_back(column->type()); + } return data; } @@ -80,7 +181,34 @@ unique_ptr OSLogLocalInit( void OSLogTableFunction(ClientContext& context, TableFunctionInput& data, DataChunk& output) { - auto oslog = data->Cast(); + auto& oslog = const_cast(data.bind_data->Cast()); + + if (!oslog.enumerator) { + NSError* error = nil; + oslog.enumerator = [oslog.store entriesEnumeratorWithOptions:0 position:nil predicate:nil error:&error]; + if (error) { + throw IOException(error.description.UTF8String); + } + } + + const auto& columns = GetColumns(); + idx_t count = 0; + + OSLogEntry* entry = nil; + while (count < output.GetCapacity() && + (entry = [oslog.enumerator nextObject])) { + if (![entry isKindOfClass:[OSLogEntryLog class]]) { + continue; + } + + for (const auto* column : columns) { + output.SetValue(column->index(), count, column->Produce(entry)); + } + ++count; + } + + // When no more output is produced, this stops producing. + output.SetCardinality(count); } } // namespace -- 2.43.5