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