1dc71348aSSergey Avseyev/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2dc71348aSSergey Avseyev/*
3cfedb1f4SSergey Avseyev *     Copyright 2018-2020 Couchbase, Inc.
4dc71348aSSergey Avseyev *
5dc71348aSSergey Avseyev *   Licensed under the Apache License, Version 2.0 (the "License");
6dc71348aSSergey Avseyev *   you may not use this file except in compliance with the License.
7dc71348aSSergey Avseyev *   You may obtain a copy of the License at
8dc71348aSSergey Avseyev *
9dc71348aSSergey Avseyev *       http://www.apache.org/licenses/LICENSE-2.0
10dc71348aSSergey Avseyev *
11dc71348aSSergey Avseyev *   Unless required by applicable law or agreed to in writing, software
12dc71348aSSergey Avseyev *   distributed under the License is distributed on an "AS IS" BASIS,
13dc71348aSSergey Avseyev *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14dc71348aSSergey Avseyev *   See the License for the specific language governing permissions and
15dc71348aSSergey Avseyev *   limitations under the License.
16dc71348aSSergey Avseyev */
17dc71348aSSergey Avseyev
18dc71348aSSergey Avseyev#ifndef LCB_TRACING_INTERNAL_H
19dc71348aSSergey Avseyev#define LCB_TRACING_INTERNAL_H
20dc71348aSSergey Avseyev
21dc71348aSSergey Avseyev#include <libcouchbase/tracing.h>
22dc71348aSSergey Avseyev#include "rnd.h"
23dc71348aSSergey Avseyev
24dc71348aSSergey Avseyev#ifdef __cplusplus
25dc71348aSSergey Avseyev
26a018a7b0SSergey Avseyev#include <queue>
276b7f4186SDavid Kelly#include <map>
286b7f4186SDavid Kelly#include <string>
29d57e4ccdSSergey Avseyev#include <memory>
30d57e4ccdSSergey Avseyev
31d57e4ccdSSergey AvseyevLCB_INTERNAL_API
32d57e4ccdSSergey Avseyevvoid lcbtrace_span_add_system_tags(lcbtrace_SPAN *span, const lcb_settings *settings, lcbtrace_THRESHOLDOPTS svc);
33d57e4ccdSSergey AvseyevLIBCOUCHBASE_API
34d57e4ccdSSergey Avseyevvoid lcbtrace_span_add_tag_str_nocopy(lcbtrace_SPAN *span, const char *name, const char *value);
35d57e4ccdSSergey Avseyevvoid lcbtrace_span_add_host_and_port(lcbtrace_SPAN *span, lcbio_CONNINFO *info);
36a018a7b0SSergey Avseyev
37dc71348aSSergey Avseyevnamespace lcb
38dc71348aSSergey Avseyev{
39dc71348aSSergey Avseyevnamespace trace
40dc71348aSSergey Avseyev{
41dc71348aSSergey Avseyev
42dc71348aSSergey Avseyevclass Span
43dc71348aSSergey Avseyev{
44dc71348aSSergey Avseyev  public:
456b7f4186SDavid Kelly    Span(lcbtrace_TRACER *tracer, const char *opname, uint64_t start, lcbtrace_REF_TYPE ref, lcbtrace_SPAN *other,
466b7f4186SDavid Kelly         void *external_span);
47bca7f437SSergey Avseyev    ~Span();
48dc71348aSSergey Avseyev
49dc71348aSSergey Avseyev    void finish(uint64_t finish);
50ec0aafceSSergey Avseyev    uint64_t duration() const
51a018a7b0SSergey Avseyev    {
52a018a7b0SSergey Avseyev        return m_finish - m_start;
53a018a7b0SSergey Avseyev    }
54a018a7b0SSergey Avseyev
55d57e4ccdSSergey Avseyev    void add_tag(const char *name, const std::string &value);
569720566aSSergey Avseyev    void add_tag(const char *name, int copy, const char *value, int copy_value);
579720566aSSergey Avseyev    void add_tag(const char *name, int copy_key, const char *value, size_t value_len, int copy_value);
58bca7f437SSergey Avseyev    void add_tag(const char *name, int copy, uint64_t value);
59bca7f437SSergey Avseyev    void add_tag(const char *name, int copy, double value);
60bca7f437SSergey Avseyev    void add_tag(const char *name, int copy, bool value);
61dc71348aSSergey Avseyev
626b7f4186SDavid Kelly    void service(lcbtrace_THRESHOLDOPTS svc);
636b7f4186SDavid Kelly    lcbtrace_THRESHOLDOPTS service() const;
646b7f4186SDavid Kelly
656b7f4186SDavid Kelly    void increment_dispatch(uint64_t dispatch_time);
666b7f4186SDavid Kelly    void increment_server(uint64_t server_time);
676b7f4186SDavid Kelly    lcbtrace_SPAN *find_outer_or_this();
686b7f4186SDavid Kelly
696b7f4186SDavid Kelly    const char *service_str() const;
706b7f4186SDavid Kelly
716b7f4186SDavid Kelly    void *external_span() const;
726b7f4186SDavid Kelly    void external_span(void *ext);
736b7f4186SDavid Kelly
746b7f4186SDavid Kelly    bool is_outer() const;
756b7f4186SDavid Kelly    void is_outer(bool outer);
766b7f4186SDavid Kelly    bool is_dispatch() const;
776b7f4186SDavid Kelly    void is_dispatch(bool dispatch);
786b7f4186SDavid Kelly    bool is_encode() const;
796b7f4186SDavid Kelly    void is_encode(bool encode);
806b7f4186SDavid Kelly    bool should_finish() const;
816b7f4186SDavid Kelly    void should_finish(bool finish);
826b7f4186SDavid Kelly
83dc71348aSSergey Avseyev    lcbtrace_TRACER *m_tracer;
84dc71348aSSergey Avseyev    std::string m_opname;
85dc71348aSSergey Avseyev    uint64_t m_span_id;
86dc71348aSSergey Avseyev    uint64_t m_start;
870b93b1bfSSergey Avseyev    uint64_t m_finish{0};
88a018a7b0SSergey Avseyev    bool m_orphaned;
89dc71348aSSergey Avseyev    Span *m_parent;
906b7f4186SDavid Kelly    void *m_extspan;
91ec0aafceSSergey Avseyev    sllist_root m_tags{};
920b93b1bfSSergey Avseyev    bool m_is_outer{false};
930b93b1bfSSergey Avseyev    bool m_is_dispatch{false};
940b93b1bfSSergey Avseyev    bool m_is_encode{false};
950b93b1bfSSergey Avseyev    bool m_should_finish{true};
960b93b1bfSSergey Avseyev    lcbtrace_THRESHOLDOPTS m_svc{LCBTRACE_THRESHOLD__MAX};
970b93b1bfSSergey Avseyev    const char *m_svc_string{nullptr};
980b93b1bfSSergey Avseyev    uint64_t m_total_dispatch{0};
990b93b1bfSSergey Avseyev    uint64_t m_last_dispatch{0};
1000b93b1bfSSergey Avseyev    uint64_t m_total_server{0};
1010b93b1bfSSergey Avseyev    uint64_t m_last_server{0};
1020b93b1bfSSergey Avseyev    uint64_t m_encode{0};
103dc71348aSSergey Avseyev};
104dc71348aSSergey Avseyev
105a018a7b0SSergey Avseyevstruct ReportedSpan {
106a018a7b0SSergey Avseyev    uint64_t duration;
107a018a7b0SSergey Avseyev    std::string payload;
108a018a7b0SSergey Avseyev
109a018a7b0SSergey Avseyev    bool operator<(const ReportedSpan &rhs) const
110a018a7b0SSergey Avseyev    {
111a018a7b0SSergey Avseyev        return duration < rhs.duration;
112a018a7b0SSergey Avseyev    }
113a018a7b0SSergey Avseyev};
114a018a7b0SSergey Avseyev
115ec0aafceSSergey Avseyevtemplate <typename T>
116ec0aafceSSergey Avseyevclass FixedQueue : private std::priority_queue<T>
117a018a7b0SSergey Avseyev{
118a018a7b0SSergey Avseyev  public:
119a018a7b0SSergey Avseyev    explicit FixedQueue(size_t capacity) : m_capacity(capacity) {}
120a018a7b0SSergey Avseyev
1210b0310e1SSergey Avseyev    void push(const T &item)
1220b0310e1SSergey Avseyev    {
123ec0aafceSSergey Avseyev        std::priority_queue<T>::push(item);
124cfcbd22eSEllis Breen        if (this->size() > m_capacity) {
125cfcbd22eSEllis Breen            this->c.pop_back();
126a018a7b0SSergey Avseyev        }
127a018a7b0SSergey Avseyev    }
128ec0aafceSSergey Avseyev    using std::priority_queue<T>::empty;
129ec0aafceSSergey Avseyev    using std::priority_queue<T>::top;
130ec0aafceSSergey Avseyev    using std::priority_queue<T>::pop;
131ec0aafceSSergey Avseyev    using std::priority_queue<T>::size;
1320b0310e1SSergey Avseyev
133a018a7b0SSergey Avseyev  private:
134a018a7b0SSergey Avseyev    size_t m_capacity;
135a018a7b0SSergey Avseyev};
136a018a7b0SSergey Avseyev
137cfcbd22eSEllis Breentypedef ReportedSpan QueueEntry;
138ec0aafceSSergey Avseyevtypedef FixedQueue<QueueEntry> FixedSpanQueue;
139a018a7b0SSergey Avseyevclass ThresholdLoggingTracer
140a018a7b0SSergey Avseyev{
141a018a7b0SSergey Avseyev    lcbtrace_TRACER *m_wrapper;
142a018a7b0SSergey Avseyev    lcb_settings *m_settings;
1436b7f4186SDavid Kelly    size_t m_threshold_queue_size;
144a018a7b0SSergey Avseyev
145cfcbd22eSEllis Breen    FixedSpanQueue m_orphans;
1466b7f4186SDavid Kelly    std::map<std::string, FixedSpanQueue> m_queues;
147a018a7b0SSergey Avseyev
1486b7f4186SDavid Kelly    void flush_queue(FixedSpanQueue &queue, const char *message, const char *service, bool warn);
149cfcbd22eSEllis Breen    QueueEntry convert(lcbtrace_SPAN *span);
150a018a7b0SSergey Avseyev
151a018a7b0SSergey Avseyev  public:
152d57e4ccdSSergey Avseyev    explicit ThresholdLoggingTracer(lcb_INSTANCE *instance);
153a018a7b0SSergey Avseyev
154a018a7b0SSergey Avseyev    lcbtrace_TRACER *wrap();
155a018a7b0SSergey Avseyev    void add_orphan(lcbtrace_SPAN *span);
156a018a7b0SSergey Avseyev    void check_threshold(lcbtrace_SPAN *span);
157300700ceSSergey Avseyev
158300700ceSSergey Avseyev    void flush_orphans();
159300700ceSSergey Avseyev    void flush_threshold();
16025525da6SSergey Avseyev    void do_flush_orphans();
16125525da6SSergey Avseyev    void do_flush_threshold();
162300700ceSSergey Avseyev
163ec0aafceSSergey Avseyev    lcb::io::Timer<ThresholdLoggingTracer, &ThresholdLoggingTracer::flush_orphans> m_oflush;
164ec0aafceSSergey Avseyev    lcb::io::Timer<ThresholdLoggingTracer, &ThresholdLoggingTracer::flush_threshold> m_tflush;
165a018a7b0SSergey Avseyev};
166a018a7b0SSergey Avseyev
167d57e4ccdSSergey Avseyevtemplate <typename COMMAND>
168d57e4ccdSSergey Avseyevlcbtrace_SPAN *start_kv_span(const lcb_settings *settings, const mc_PACKET *packet, std::shared_ptr<COMMAND> cmd)
169d57e4ccdSSergey Avseyev{
170d57e4ccdSSergey Avseyev    if (settings == nullptr || settings->tracer == nullptr) {
171d57e4ccdSSergey Avseyev        return nullptr;
172d57e4ccdSSergey Avseyev    }
173d57e4ccdSSergey Avseyev    lcbtrace_SPAN *span;
174d57e4ccdSSergey Avseyev    lcbtrace_SPAN *parent_span = cmd->parent_span();
175d57e4ccdSSergey Avseyev    if (parent_span != nullptr && parent_span->is_outer() && settings->tracer->flags & LCBTRACE_F_THRESHOLD) {
176d57e4ccdSSergey Avseyev        span = parent_span;
177d57e4ccdSSergey Avseyev        span->should_finish(false);
178d57e4ccdSSergey Avseyev    } else {
179d57e4ccdSSergey Avseyev        lcbtrace_REF ref;
180d57e4ccdSSergey Avseyev        ref.type = LCBTRACE_REF_CHILD_OF;
181d57e4ccdSSergey Avseyev        ref.span = parent_span;
182d57e4ccdSSergey Avseyev        bool is_dispatch = (parent_span != nullptr && parent_span->is_outer());
183d57e4ccdSSergey Avseyev        span = lcbtrace_span_start((settings)->tracer,
184d57e4ccdSSergey Avseyev                                   is_dispatch ? LCBTRACE_OP_DISPATCH_TO_SERVER : cmd->operation_name().c_str(),
185d57e4ccdSSergey Avseyev                                   LCBTRACE_NOW, &ref);
186d57e4ccdSSergey Avseyev        span->should_finish(true);
187d57e4ccdSSergey Avseyev        span->is_outer(!is_dispatch);
1886b7f4186SDavid Kelly    }
189d57e4ccdSSergey Avseyev    span->is_dispatch(true);
190d57e4ccdSSergey Avseyev    std::string operation_id = std::to_string(packet->opaque);
191d57e4ccdSSergey Avseyev    lcbtrace_span_add_tag_str(span, LCBTRACE_TAG_OPERATION_ID, operation_id.c_str());
192d57e4ccdSSergey Avseyev    lcbtrace_span_add_system_tags(span, settings, LCBTRACE_THRESHOLD_KV);
193d57e4ccdSSergey Avseyev    span->add_tag(LCBTRACE_TAG_SCOPE, cmd->collection().scope());
194d57e4ccdSSergey Avseyev    span->add_tag(LCBTRACE_TAG_COLLECTION, cmd->collection().collection());
195d57e4ccdSSergey Avseyev    span->add_tag(LCBTRACE_TAG_OPERATION, cmd->operation_name());
196d57e4ccdSSergey Avseyev    return span;
197d57e4ccdSSergey Avseyev}
1986b7f4186SDavid Kelly
199d57e4ccdSSergey Avseyevvoid finish_kv_span(const mc_PIPELINE *pipeline, const mc_PACKET *request_pkt, const MemcachedResponse *response_pkt);
2006b7f4186SDavid Kelly
201d57e4ccdSSergey Avseyevtemplate <typename COMMAND>
202d57e4ccdSSergey Avseyevlcbtrace_SPAN *start_kv_span_with_durability(const lcb_settings *settings, const mc_PACKET *packet,
203d57e4ccdSSergey Avseyev                                             std::shared_ptr<COMMAND> cmd)
204d57e4ccdSSergey Avseyev{
205d57e4ccdSSergey Avseyev    lcbtrace_SPAN *span = start_kv_span(settings, packet, cmd);
206d57e4ccdSSergey Avseyev    if (span != nullptr && cmd->durability_level() != LCB_DURABILITYLEVEL_NONE) {
207d57e4ccdSSergey Avseyev        span->add_tag(LCBTRACE_TAG_DURABILITY, 0, dur_level_to_string(cmd->durability_level()), 0);
208dc71348aSSergey Avseyev    }
209d57e4ccdSSergey Avseyev    return span;
210d57e4ccdSSergey Avseyev}
211dc71348aSSergey Avseyev
212d57e4ccdSSergey Avseyevtemplate <typename COMMAND>
213d57e4ccdSSergey Avseyevlcbtrace_SPAN *start_http_span(const lcb_settings *settings, const COMMAND *cmd)
214d57e4ccdSSergey Avseyev{
215d57e4ccdSSergey Avseyev    if (settings == nullptr || settings->tracer == nullptr) {
216d57e4ccdSSergey Avseyev        return nullptr;
217d57e4ccdSSergey Avseyev    }
218d57e4ccdSSergey Avseyev    lcbtrace_SPAN *span;
219d57e4ccdSSergey Avseyev    lcbtrace_SPAN *parent_span = cmd->parent_span();
220d57e4ccdSSergey Avseyev    if (parent_span != nullptr && parent_span->is_outer() && settings->tracer->flags & LCBTRACE_F_THRESHOLD) {
221d57e4ccdSSergey Avseyev        span = parent_span;
222d57e4ccdSSergey Avseyev        span->should_finish(false);
223d57e4ccdSSergey Avseyev    } else {
224d57e4ccdSSergey Avseyev        lcbtrace_REF ref;
225d57e4ccdSSergey Avseyev        ref.type = LCBTRACE_REF_CHILD_OF;
226d57e4ccdSSergey Avseyev        ref.span = parent_span;
227d57e4ccdSSergey Avseyev        bool is_dispatch = (parent_span != nullptr && parent_span->is_outer());
228d57e4ccdSSergey Avseyev        span = lcbtrace_span_start((settings)->tracer,
229d57e4ccdSSergey Avseyev                                   is_dispatch ? LCBTRACE_OP_DISPATCH_TO_SERVER : cmd->operation_name().c_str(),
230d57e4ccdSSergey Avseyev                                   LCBTRACE_NOW, &ref);
231d57e4ccdSSergey Avseyev        span->should_finish(true);
232d57e4ccdSSergey Avseyev        span->is_outer(!is_dispatch);
2336b7f4186SDavid Kelly    }
234d57e4ccdSSergey Avseyev    span->is_dispatch(true);
235d57e4ccdSSergey Avseyev    lcbtrace_span_add_tag_str(span, LCBTRACE_TAG_OPERATION_ID, cmd->client_context_id().c_str());
236d57e4ccdSSergey Avseyev    lcbtrace_span_add_system_tags(span, settings, cmd->service());
237d57e4ccdSSergey Avseyev    span->add_tag(LCBTRACE_TAG_OPERATION, cmd->operation_name());
238d57e4ccdSSergey Avseyev    return span;
239d57e4ccdSSergey Avseyev}
2406b7f4186SDavid Kelly
241d57e4ccdSSergey Avseyevtemplate <typename COMMAND>
242d57e4ccdSSergey Avseyevlcbtrace_SPAN *start_http_span_with_statement(const lcb_settings *settings, const COMMAND *cmd,
243d57e4ccdSSergey Avseyev                                              const std::string &statement)
244d57e4ccdSSergey Avseyev{
245d57e4ccdSSergey Avseyev    lcbtrace_SPAN *span = start_http_span(settings, cmd);
246d57e4ccdSSergey Avseyev    if (span != nullptr && !statement.empty()) {
247d57e4ccdSSergey Avseyev        span->add_tag(LCBTRACE_TAG_STATEMENT, statement);
2486b7f4186SDavid Kelly    }
249d57e4ccdSSergey Avseyev    return span;
250d57e4ccdSSergey Avseyev}
2516b7f4186SDavid Kelly
252d57e4ccdSSergey Avseyevtemplate <typename COMMAND>
253d57e4ccdSSergey Avseyevvoid finish_http_span(lcbtrace_SPAN *span, const COMMAND *cmd)
254d57e4ccdSSergey Avseyev{
255d57e4ccdSSergey Avseyev    if (span != nullptr) {
256d57e4ccdSSergey Avseyev        span->find_outer_or_this()->add_tag(LCBTRACE_TAG_RETRIES, 0, (uint64_t)cmd->retries());
257d57e4ccdSSergey Avseyev        if (span->should_finish()) {
258d57e4ccdSSergey Avseyev            lcbtrace_span_finish(span, LCBTRACE_NOW);
259d57e4ccdSSergey Avseyev        }
2606b7f4186SDavid Kelly    }
261dc71348aSSergey Avseyev}
262d57e4ccdSSergey Avseyev
263d57e4ccdSSergey Avseyev} // namespace trace
264d57e4ccdSSergey Avseyev} // namespace lcb
265d57e4ccdSSergey Avseyev
266d57e4ccdSSergey Avseyevextern "C" {
267d57e4ccdSSergey Avseyev#endif /* __cplusplus */
268d57e4ccdSSergey AvseyevLCB_INTERNAL_API
269d57e4ccdSSergey Avseyevvoid lcbtrace_span_set_orphaned(lcbtrace_SPAN *span, int val);
270d57e4ccdSSergey Avseyev
271d57e4ccdSSergey Avseyevconst char *dur_level_to_string(lcb_DURABILITY_LEVEL dur_level);
272d57e4ccdSSergey Avseyev#ifdef __cplusplus
273d57e4ccdSSergey Avseyev}
2746b7f4186SDavid Kelly#endif /* __cplusplus*/
275dc71348aSSergey Avseyev#endif /* LCB_TRACING_INTERNAL_H */
276