зеркало из https://github.com/microsoft/CCF.git
Get metrics v1 (#3025)
This commit is contained in:
Родитель
674b5c1e80
Коммит
41cadaf415
|
@ -8,7 +8,8 @@ trigger:
|
|||
jobs:
|
||||
- job: build_and_publish_docs
|
||||
container: ccfciteam/ccf-ci:oe0.17.2-docker-cli
|
||||
pool: 1es-dv4-focal
|
||||
pool:
|
||||
vmImage: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
|
|
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
### Added
|
||||
|
||||
- Receipts now include the endorsed certificate of the node, as well as its node id, for convenience.
|
||||
- `get_metrics_v1` API to `BaseEndpointRegistry` for applications that do not make use of builtins and want to version or customise metrics output.
|
||||
|
||||
## [2.0.0-dev4]
|
||||
|
||||
|
|
|
@ -29,6 +29,14 @@ Application Endpoint Registration
|
|||
:project: CCF
|
||||
:members:
|
||||
|
||||
.. doxygenstruct:: ccf::EndpointMetricsEntry
|
||||
:project: CCF
|
||||
:members:
|
||||
|
||||
.. doxygenstruct:: ccf::EndpointMetrics
|
||||
:project: CCF
|
||||
:members:
|
||||
|
||||
.. doxygenclass:: ccf::BaseEndpointRegistry
|
||||
:project: CCF
|
||||
:members:
|
||||
|
|
|
@ -7,7 +7,18 @@
|
|||
],
|
||||
"type": "string"
|
||||
},
|
||||
"EndpointMetrics__Entry": {
|
||||
"EndpointMetrics": {
|
||||
"properties": {
|
||||
"metrics": {
|
||||
"$ref": "#/components/schemas/EndpointMetricsEntry_array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metrics"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"EndpointMetricsEntry": {
|
||||
"properties": {
|
||||
"calls": {
|
||||
"$ref": "#/components/schemas/uint64"
|
||||
|
@ -38,23 +49,12 @@
|
|||
],
|
||||
"type": "object"
|
||||
},
|
||||
"EndpointMetrics__Entry_array": {
|
||||
"EndpointMetricsEntry_array": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/EndpointMetrics__Entry"
|
||||
"$ref": "#/components/schemas/EndpointMetricsEntry"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"EndpointMetrics__Out": {
|
||||
"properties": {
|
||||
"metrics": {
|
||||
"$ref": "#/components/schemas/EndpointMetrics__Entry_array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metrics"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"EntityId": {
|
||||
"format": "hex",
|
||||
"pattern": "^[a-f0-9]{64}$",
|
||||
|
@ -338,7 +338,7 @@
|
|||
"info": {
|
||||
"description": "This CCF sample app implements a simple logging application, securely recording messages at client-specified IDs. It demonstrates most of the features available to CCF apps.",
|
||||
"title": "CCF Sample Logging App",
|
||||
"version": "1.1.0"
|
||||
"version": "1.2.0"
|
||||
},
|
||||
"openapi": "3.0.0",
|
||||
"paths": {
|
||||
|
@ -365,7 +365,7 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/EndpointMetrics__Out"
|
||||
"$ref": "#/components/schemas/EndpointMetrics"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -39,7 +39,18 @@
|
|||
],
|
||||
"type": "string"
|
||||
},
|
||||
"EndpointMetrics__Entry": {
|
||||
"EndpointMetrics": {
|
||||
"properties": {
|
||||
"metrics": {
|
||||
"$ref": "#/components/schemas/EndpointMetricsEntry_array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metrics"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"EndpointMetricsEntry": {
|
||||
"properties": {
|
||||
"calls": {
|
||||
"$ref": "#/components/schemas/uint64"
|
||||
|
@ -70,23 +81,12 @@
|
|||
],
|
||||
"type": "object"
|
||||
},
|
||||
"EndpointMetrics__Entry_array": {
|
||||
"EndpointMetricsEntry_array": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/EndpointMetrics__Entry"
|
||||
"$ref": "#/components/schemas/EndpointMetricsEntry"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"EndpointMetrics__Out": {
|
||||
"properties": {
|
||||
"metrics": {
|
||||
"$ref": "#/components/schemas/EndpointMetrics__Entry_array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metrics"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"EntityId": {
|
||||
"format": "hex",
|
||||
"pattern": "^[a-f0-9]{64}$",
|
||||
|
@ -400,7 +400,7 @@
|
|||
"info": {
|
||||
"description": "This API is used to submit and query proposals which affect CCF's public governance tables.",
|
||||
"title": "CCF Governance API",
|
||||
"version": "2.1.0"
|
||||
"version": "2.2.0"
|
||||
},
|
||||
"openapi": "3.0.0",
|
||||
"paths": {
|
||||
|
@ -472,7 +472,7 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/EndpointMetrics__Out"
|
||||
"$ref": "#/components/schemas/EndpointMetrics"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,7 +7,18 @@
|
|||
],
|
||||
"type": "string"
|
||||
},
|
||||
"EndpointMetrics__Entry": {
|
||||
"EndpointMetrics": {
|
||||
"properties": {
|
||||
"metrics": {
|
||||
"$ref": "#/components/schemas/EndpointMetricsEntry_array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metrics"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"EndpointMetricsEntry": {
|
||||
"properties": {
|
||||
"calls": {
|
||||
"$ref": "#/components/schemas/uint64"
|
||||
|
@ -38,23 +49,12 @@
|
|||
],
|
||||
"type": "object"
|
||||
},
|
||||
"EndpointMetrics__Entry_array": {
|
||||
"EndpointMetricsEntry_array": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/EndpointMetrics__Entry"
|
||||
"$ref": "#/components/schemas/EndpointMetricsEntry"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"EndpointMetrics__Out": {
|
||||
"properties": {
|
||||
"metrics": {
|
||||
"$ref": "#/components/schemas/EndpointMetrics__Entry_array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metrics"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"EntityId": {
|
||||
"format": "hex",
|
||||
"pattern": "^[a-f0-9]{64}$",
|
||||
|
@ -494,7 +494,7 @@
|
|||
"info": {
|
||||
"description": "This API provides public, uncredentialed access to service and node state.",
|
||||
"title": "CCF Public Node API",
|
||||
"version": "2.1.0"
|
||||
"version": "2.2.0"
|
||||
},
|
||||
"openapi": "3.0.0",
|
||||
"paths": {
|
||||
|
@ -521,7 +521,7 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/EndpointMetrics__Out"
|
||||
"$ref": "#/components/schemas/EndpointMetrics"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -156,5 +156,10 @@ namespace ccf
|
|||
/** Get untrusted time from the host of the currently executing node.
|
||||
*/
|
||||
ApiResult get_untrusted_host_time_v1(::timespec& time);
|
||||
|
||||
/** Get usage metrics from endpoints under the registry, including
|
||||
* number of calls, errors, failures and retries.
|
||||
*/
|
||||
ApiResult get_metrics_v1(EndpointMetrics& endpoint_metrics);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "ds/openapi.h"
|
||||
#include "http/http_consts.h"
|
||||
#include "node/certs.h"
|
||||
#include "node/endpoint_metrics.h"
|
||||
#include "node/rpc/serialization.h"
|
||||
|
||||
#include <charconv>
|
||||
|
|
|
@ -1191,7 +1191,7 @@ namespace loggingapp
|
|||
"This CCF sample app implements a simple logging application, securely "
|
||||
"recording messages at client-specified IDs. It demonstrates most of "
|
||||
"the features available to CCF apps.";
|
||||
logger_handlers.openapi_info.document_version = "1.1.0";
|
||||
logger_handlers.openapi_info.document_version = "1.2.0";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -280,4 +280,25 @@ namespace ccf
|
|||
|
||||
return ApiResult::OK;
|
||||
}
|
||||
|
||||
ApiResult BaseEndpointRegistry::get_metrics_v1(
|
||||
EndpointMetrics& endpoint_metrics)
|
||||
{
|
||||
endpoint_metrics.metrics.clear();
|
||||
std::lock_guard<std::mutex> guard(metrics_lock);
|
||||
for (const auto& [path, verb_metrics] : metrics)
|
||||
{
|
||||
for (const auto& [verb, metric] : verb_metrics)
|
||||
{
|
||||
endpoint_metrics.metrics.push_back(
|
||||
{path,
|
||||
verb,
|
||||
metric.calls,
|
||||
metric.errors,
|
||||
metric.failures,
|
||||
metric.retries});
|
||||
}
|
||||
}
|
||||
return ApiResult::OK;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,29 +197,26 @@ namespace ccf
|
|||
.install();
|
||||
|
||||
auto endpoint_metrics_fn = [this](auto&, nlohmann::json&&) {
|
||||
std::lock_guard<std::mutex> guard(metrics_lock);
|
||||
EndpointMetrics::Out out;
|
||||
for (const auto& [path, verb_metrics] : metrics)
|
||||
EndpointMetrics out;
|
||||
const auto result = get_metrics_v1(out);
|
||||
if (result == ccf::ApiResult::OK)
|
||||
{
|
||||
for (const auto& [verb, metric] : verb_metrics)
|
||||
{
|
||||
out.metrics.push_back(
|
||||
{path,
|
||||
verb,
|
||||
metric.calls,
|
||||
metric.errors,
|
||||
metric.failures,
|
||||
metric.retries});
|
||||
}
|
||||
return make_success(out);
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_error(
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
ccf::errors::InternalError,
|
||||
fmt::format("Error code: {}", ccf::api_result_to_str(result)));
|
||||
}
|
||||
return make_success(out);
|
||||
};
|
||||
make_command_endpoint(
|
||||
"/api/metrics",
|
||||
HTTP_GET,
|
||||
json_command_adapter(endpoint_metrics_fn),
|
||||
no_auth_required)
|
||||
.set_auto_schema<void, EndpointMetrics::Out>()
|
||||
.set_auto_schema<void, EndpointMetrics>()
|
||||
.set_execute_outside_consensus(
|
||||
ccf::endpoints::ExecuteOutsideConsensus::Locally)
|
||||
.install();
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the Apache 2.0 License.
|
||||
|
||||
#pragma once
|
||||
#include "ds/json.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ccf
|
||||
{
|
||||
struct EndpointMetricsEntry
|
||||
{
|
||||
/// Endpoint path
|
||||
std::string path;
|
||||
/// Endpoint method
|
||||
std::string method;
|
||||
/// Number of calls since node start
|
||||
size_t calls = 0;
|
||||
/// Number of errors (4xx) since node start
|
||||
size_t errors = 0;
|
||||
/// Number of failures (5xx) since node start
|
||||
size_t failures = 0;
|
||||
/// Number of transaction retries caused by
|
||||
/// conflicts since node start
|
||||
size_t retries = 0;
|
||||
};
|
||||
|
||||
struct EndpointMetrics
|
||||
{
|
||||
/// Metrics for all endpoints in the frontend
|
||||
std::vector<EndpointMetricsEntry> metrics;
|
||||
};
|
||||
|
||||
DECLARE_JSON_TYPE(EndpointMetricsEntry)
|
||||
DECLARE_JSON_REQUIRED_FIELDS(
|
||||
EndpointMetricsEntry, path, method, calls, errors, failures, retries)
|
||||
DECLARE_JSON_TYPE(EndpointMetrics)
|
||||
DECLARE_JSON_REQUIRED_FIELDS(EndpointMetrics, metrics)
|
||||
}
|
|
@ -91,24 +91,6 @@ namespace ccf
|
|||
using Out = nlohmann::json;
|
||||
};
|
||||
|
||||
struct EndpointMetrics
|
||||
{
|
||||
struct Entry
|
||||
{
|
||||
std::string path;
|
||||
std::string method;
|
||||
size_t calls = 0;
|
||||
size_t errors = 0;
|
||||
size_t failures = 0;
|
||||
size_t retries = 0;
|
||||
};
|
||||
|
||||
struct Out
|
||||
{
|
||||
std::vector<Entry> metrics;
|
||||
};
|
||||
};
|
||||
|
||||
struct VerifyReceipt
|
||||
{
|
||||
struct In
|
||||
|
|
|
@ -482,7 +482,7 @@ namespace ccf
|
|||
openapi_info.description =
|
||||
"This API is used to submit and query proposals which affect CCF's "
|
||||
"public governance tables.";
|
||||
openapi_info.document_version = "2.1.0";
|
||||
openapi_info.document_version = "2.2.0";
|
||||
}
|
||||
|
||||
static std::optional<MemberId> get_caller_member_id(
|
||||
|
|
|
@ -285,7 +285,7 @@ namespace ccf
|
|||
openapi_info.description =
|
||||
"This API provides public, uncredentialed access to service and node "
|
||||
"state.";
|
||||
openapi_info.document_version = "2.1.0";
|
||||
openapi_info.document_version = "2.2.0";
|
||||
}
|
||||
|
||||
void init_handlers() override
|
||||
|
|
|
@ -117,12 +117,6 @@ namespace ccf
|
|||
DECLARE_JSON_TYPE(GetNodes::Out)
|
||||
DECLARE_JSON_REQUIRED_FIELDS(GetNodes::Out, nodes)
|
||||
|
||||
DECLARE_JSON_TYPE(EndpointMetrics::Entry)
|
||||
DECLARE_JSON_REQUIRED_FIELDS(
|
||||
EndpointMetrics::Entry, path, method, calls, errors, failures, retries)
|
||||
DECLARE_JSON_TYPE(EndpointMetrics::Out)
|
||||
DECLARE_JSON_REQUIRED_FIELDS(EndpointMetrics::Out, metrics)
|
||||
|
||||
DECLARE_JSON_TYPE(VerifyReceipt::In)
|
||||
DECLARE_JSON_REQUIRED_FIELDS(VerifyReceipt::In, receipt)
|
||||
DECLARE_JSON_TYPE(VerifyReceipt::Out)
|
||||
|
|
Загрузка…
Ссылка в новой задаче