Skip to content

Commit f6dedb2

Browse files
authored
feat: Add support for finding the log event that's closest to a target timestamp. (#42)
1 parent b4a6791 commit f6dedb2

6 files changed

+91
-1
lines changed

src/clp_ffi_js/ir/StreamReader.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ EMSCRIPTEN_BINDINGS(ClpStreamReader) {
129129
"Array<[string, bigint, number, number]>"
130130
);
131131
emscripten::register_type<clp_ffi_js::ir::FilteredLogEventMapTsType>("number[] | null");
132+
emscripten::register_type<clp_ffi_js::ir::NullableLogEventIdx>("number | null");
132133
emscripten::class_<clp_ffi_js::ir::StreamReader>("ClpStreamReader")
133134
.constructor(
134135
&clp_ffi_js::ir::StreamReader::create,
@@ -145,7 +146,11 @@ EMSCRIPTEN_BINDINGS(ClpStreamReader) {
145146
)
146147
.function("filterLogEvents", &clp_ffi_js::ir::StreamReader::filter_log_events)
147148
.function("deserializeStream", &clp_ffi_js::ir::StreamReader::deserialize_stream)
148-
.function("decodeRange", &clp_ffi_js::ir::StreamReader::decode_range);
149+
.function("decodeRange", &clp_ffi_js::ir::StreamReader::decode_range)
150+
.function(
151+
"findNearestLogEventByTimestamp",
152+
&clp_ffi_js::ir::StreamReader::find_nearest_log_event_by_timestamp
153+
);
149154
}
150155
} // namespace
151156

src/clp_ffi_js/ir/StreamReader.hpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <type_traits>
1212
#include <vector>
1313

14+
#include <clp/ir/types.hpp>
1415
#include <clp/streaming_compression/zstd/Decompressor.hpp>
1516
#include <clp/type_utils.hpp>
1617
#include <emscripten/em_asm.h>
@@ -29,6 +30,7 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(ReaderOptions);
2930
// JS types used as outputs
3031
EMSCRIPTEN_DECLARE_VAL_TYPE(DecodedResultsTsType);
3132
EMSCRIPTEN_DECLARE_VAL_TYPE(FilteredLogEventMapTsType);
33+
EMSCRIPTEN_DECLARE_VAL_TYPE(NullableLogEventIdx);
3234

3335
enum class StreamType : uint8_t {
3436
Structured,
@@ -124,6 +126,27 @@ class StreamReader {
124126
[[nodiscard]] virtual auto decode_range(size_t begin_idx, size_t end_idx, bool use_filter) const
125127
-> DecodedResultsTsType = 0;
126128

129+
/**
130+
* Finds the log event, L, where if we assume:
131+
*
132+
* - the collection of log events is sorted in chronological order;
133+
* - and we insert a marker log event, M, with timestamp `target_ts` into the collection (if log
134+
* events with timestamp `target_ts` already exist in the collection, M should be inserted
135+
* after them).
136+
*
137+
* L is the event just before M, if M is not the first event in the collection; otherwise L is
138+
* the event just after M.
139+
*
140+
* NOTE: If the collection of log events isn't in chronological order, this method has undefined
141+
* behaviour.
142+
*
143+
* @param target_ts
144+
* @return The index of the log event L.
145+
*/
146+
[[nodiscard]] virtual auto find_nearest_log_event_by_timestamp(
147+
clp::ir::epoch_time_ms_t target_ts
148+
) -> NullableLogEventIdx = 0;
149+
127150
protected:
128151
explicit StreamReader() = default;
129152

@@ -172,6 +195,20 @@ class StreamReader {
172195
LogLevelFilterTsType const& log_level_filter,
173196
LogEvents<LogEvent> const& log_events
174197
) -> void;
198+
199+
/**
200+
* Templated implementation of `find_nearest_log_event_by_timestamp`.
201+
*
202+
* @tparam LogEvent
203+
* @param log_events
204+
* @param target_ts
205+
* @return See `find_nearest_log_event_by_timestamp`.
206+
*/
207+
template <typename LogEvent>
208+
auto generic_find_nearest_log_event_by_timestamp(
209+
LogEvents<LogEvent> const& log_events,
210+
clp::ir::epoch_time_ms_t target_ts
211+
) -> NullableLogEventIdx;
175212
};
176213

177214
template <typename LogEvent, typename ToStringFunc>
@@ -258,6 +295,34 @@ auto StreamReader::generic_filter_log_events(
258295
}
259296
}
260297
}
298+
299+
template <typename LogEvent>
300+
auto StreamReader::generic_find_nearest_log_event_by_timestamp(
301+
LogEvents<LogEvent> const& log_events,
302+
clp::ir::epoch_time_ms_t target_ts
303+
) -> NullableLogEventIdx {
304+
if (log_events.empty()) {
305+
return NullableLogEventIdx{emscripten::val::null()};
306+
}
307+
308+
// Find the log event whose timestamp is just after `target_ts`
309+
auto first_greater_it{std::upper_bound(
310+
log_events.begin(),
311+
log_events.end(),
312+
target_ts,
313+
[](clp::ir::epoch_time_ms_t ts, LogEventWithFilterData<LogEvent> const& log_event) {
314+
return ts < log_event.get_timestamp();
315+
}
316+
)};
317+
318+
if (first_greater_it == log_events.begin()) {
319+
return NullableLogEventIdx{emscripten::val(0)};
320+
}
321+
322+
auto const first_greater_idx{std::distance(log_events.begin(), first_greater_it)};
323+
324+
return NullableLogEventIdx{emscripten::val(first_greater_idx - 1)};
325+
}
261326
} // namespace clp_ffi_js::ir
262327

263328
#endif // CLP_FFI_JS_IR_STREAMREADER_HPP

src/clp_ffi_js/ir/StructuredIrStreamReader.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <clp/Array.hpp>
1212
#include <clp/ErrorCode.hpp>
1313
#include <clp/ffi/ir_stream/Deserializer.hpp>
14+
#include <clp/ir/types.hpp>
1415
#include <clp/TraceableException.hpp>
1516
#include <emscripten/val.h>
1617
#include <json/single_include/nlohmann/json.hpp>
@@ -162,6 +163,12 @@ auto StructuredIrStreamReader::decode_range(size_t begin_idx, size_t end_idx, bo
162163
);
163164
}
164165

166+
auto StructuredIrStreamReader::find_nearest_log_event_by_timestamp(
167+
clp::ir::epoch_time_ms_t const target_ts
168+
) -> NullableLogEventIdx {
169+
return generic_find_nearest_log_event_by_timestamp(*m_deserialized_log_events, target_ts);
170+
}
171+
165172
StructuredIrStreamReader::StructuredIrStreamReader(
166173
StreamReaderDataContext<StructuredIrDeserializer>&& stream_reader_data_context,
167174
std::shared_ptr<StructuredLogEvents> deserialized_log_events

src/clp_ffi_js/ir/StructuredIrStreamReader.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <clp/Array.hpp>
99
#include <clp/ffi/ir_stream/Deserializer.hpp>
1010
#include <clp/ffi/SchemaTree.hpp>
11+
#include <clp/ir/types.hpp>
1112
#include <emscripten/val.h>
1213

1314
#include <clp_ffi_js/ir/LogEventWithFilterData.hpp>
@@ -74,6 +75,9 @@ class StructuredIrStreamReader : public StreamReader {
7475
[[nodiscard]] auto decode_range(size_t begin_idx, size_t end_idx, bool use_filter) const
7576
-> DecodedResultsTsType override;
7677

78+
[[nodiscard]] auto find_nearest_log_event_by_timestamp(clp::ir::epoch_time_ms_t target_ts
79+
) -> NullableLogEventIdx override;
80+
7781
private:
7882
// Constructor
7983
explicit StructuredIrStreamReader(

src/clp_ffi_js/ir/UnstructuredIrStreamReader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ auto UnstructuredIrStreamReader::decode_range(size_t begin_idx, size_t end_idx,
158158
);
159159
}
160160

161+
auto UnstructuredIrStreamReader::find_nearest_log_event_by_timestamp(
162+
clp::ir::epoch_time_ms_t const target_ts
163+
) -> NullableLogEventIdx {
164+
return generic_find_nearest_log_event_by_timestamp(m_encoded_log_events, target_ts);
165+
}
166+
161167
UnstructuredIrStreamReader::UnstructuredIrStreamReader(
162168
StreamReaderDataContext<UnstructuredIrDeserializer>&& stream_reader_data_context
163169
)

src/clp_ffi_js/ir/UnstructuredIrStreamReader.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ class UnstructuredIrStreamReader : public StreamReader {
7171
[[nodiscard]] auto decode_range(size_t begin_idx, size_t end_idx, bool use_filter) const
7272
-> DecodedResultsTsType override;
7373

74+
[[nodiscard]] auto find_nearest_log_event_by_timestamp(clp::ir::epoch_time_ms_t target_ts
75+
) -> NullableLogEventIdx override;
76+
7477
private:
7578
// Constructor
7679
explicit UnstructuredIrStreamReader(

0 commit comments

Comments
 (0)