Merged PR 282183: Add support for logging providers

Add support for logging providers
This commit is contained in:
Asi Bross 2017-06-05 17:34:45 +00:00 коммит произвёл Asi Bross
Родитель f92c2e4b3f
Коммит f6026033b5
27 изменённых файлов: 944 добавлений и 50 удалений

Просмотреть файл

@ -3,20 +3,30 @@
#include <iostream>
#include <exception>
#define NAPA_ASSERT(condition, message) do { \
if (!(condition)) { \
std::cerr << "Assertion failed: `" \
<< #condition \
<< "`, file " \
<< __FILE__ \
<< ", line " \
<< __LINE__ \
<< " : " \
<< message \
<< "." \
<< std::endl; \
std::terminate(); \
} \
#include <stdarg.h>
/// <summary> The maximum message length of a single assert call. Anything over will be truncated. </summary>
static constexpr size_t MAX_ASSERT_MESSAGE_SIZE = 512;
inline void OutputAssertMessage(const char* condition, const char* file, int line, const char* format, ...) {
char message[MAX_ASSERT_MESSAGE_SIZE];
va_list args;
va_start(args, format);
int size = vsnprintf(message, MAX_ASSERT_MESSAGE_SIZE, format, args);
va_end(args);
if (size >= 0) {
std::cerr << "Assertion failed: `" << condition << "`, file " << file << ", line " << line
<< " : " << message << "." << std::endl;
}
}
#define NAPA_ASSERT(condition, format, ...) do { \
if (!(condition)) { \
OutputAssertMessage(#condition, __FILE__, __LINE__, format, __VA_ARGS__); \
std::terminate(); \
} \
} while (false)
#define NAPA_FAIL(message) NAPA_ASSERT(false, message)

Просмотреть файл

@ -1,5 +1,7 @@
#pragma once
#include <napa/exports.h>
#include <stdint.h>
namespace napa {
@ -7,7 +9,7 @@ namespace providers {
/// <summary> Enumeration of metric type. </summary>
enum class MetricType {
Number,
Number = 0,
Rate,
Percentile,
};
@ -94,7 +96,8 @@ namespace providers {
/// The IMetric class returned is owned and cached by this class.
/// Callers are not required to call destroy() on the Metric.
/// </remarks>
virtual Metric* GetMetric(const char* section,
virtual Metric* GetMetric(
const char* section,
const char* name,
MetricType type,
size_t dimensions,
@ -109,6 +112,9 @@ namespace providers {
virtual ~MetricProvider() = default;
};
/// <summary> Exports a getter function for retrieves the configured metric provider. </summary>
NAPA_API MetricProvider& GetMetricProvider();
typedef MetricProvider* (*CreateMetricProvider)();
}
}

Просмотреть файл

@ -1,6 +1,8 @@
import { log } from './log';
import * as memory from './memory';
import * as metric from './metric';
import * as runtime from './runtime';
import * as transport from './transport';
import * as zone from './zone';
export { memory, runtime, transport, zone };
export { log, memory, metric, runtime, transport, zone };

75
lib/log.ts Normal file
Просмотреть файл

@ -0,0 +1,75 @@
let binding = require('./binding');
export interface LogFunction {
(message: string): void;
(section: string, message: string): void;
(section: string, traceId: string, message: string): void;
}
export interface Log extends LogFunction {
err(message: string): void;
err(section: string, message: string): void;
err(section: string, traceId: string, message: string): void;
warn(message: string): void;
warn(section: string, message: string): void;
warn(section: string, traceId: string, message: string): void;
info(message: string): void;
info(section: string, message: string): void;
info(section: string, traceId: string, message: string): void;
debug(message: string): void;
debug(section: string, message: string): void;
debug(section: string, traceId: string, message: string): void;
}
export let log: Log = createLogObject();
enum LogLevel {
Error = 0,
Warning = 1,
Info = 2,
Debug = 3,
}
function dispatchLog(level: LogLevel, arg1: string, arg2?: string, arg3?: string) {
if (arg3 != undefined) {
binding.log(level, arg1, arg2, arg3);
}
else if (arg2 != undefined) {
binding.log(level, arg1, undefined, arg2);
}
else {
binding.log(level, undefined, undefined, arg1);
}
}
function createLogObject() : Log {
// napa.log()
let logObj: any = function(arg1: string, arg2?: string, arg3?: string) {
dispatchLog(LogLevel.Info, arg1, arg2, arg3);
}
// napa.log.err()
logObj.err = function(arg1: string, arg2?: string, arg3?: string) {
dispatchLog(LogLevel.Error, arg1, arg2, arg3);
}
// napa.log.warn()
logObj.warn = function(arg1: string, arg2?: string, arg3?: string) {
dispatchLog(LogLevel.Warning, arg1, arg2, arg3);
}
// napa.log.info()
logObj.info = function(arg1: string, arg2?: string, arg3?: string) {
dispatchLog(LogLevel.Info, arg1, arg2, arg3);
}
// napa.log.debug()
logObj.debug = function(arg1: string, arg2?: string, arg3?: string) {
dispatchLog(LogLevel.Debug, arg1, arg2, arg3);
}
return logObj;
}

37
lib/metric.ts Normal file
Просмотреть файл

@ -0,0 +1,37 @@
let binding = require('./binding');
export enum MetricType {
Number = 0,
Rate,
Percentile,
}
export interface Metric {
section: string;
name: string;
set(value: number, dimensions?: string[]): void;
increment(value: number, dimensions?: string[]): void;
decrement(value: number, dimensions?: string[]): void;
}
/// <summary> A cache for metric wraps. </summary>
var _metricsCache: { [key: string]: Metric } = {};
export function get(section: string, name: string, type: MetricType, dimensions: string[] = []) : Metric {
let key: string = (section ? section : "") + "_" + (name ? name : "");
// Check cache first
let metricWrap: Metric = _metricsCache[key];
if (metricWrap) {
return metricWrap;
}
// Add to cache
metricWrap = new binding.MetricWrap(section, name, type, dimensions);
metricWrap.section = section;
metricWrap.name = name;
_metricsCache[key] = metricWrap;
return metricWrap;
}

Просмотреть файл

@ -29,6 +29,7 @@ export function setPlatformSettings(settings: PlatformSettings) {
}
_platformSettings = settings;
initialize();
}
export function initialize() {

Просмотреть файл

@ -19,6 +19,7 @@ export function create(id: string, settings?: zone.ZoneSettings) : zone.Zone {
/// <summary> Returns the zone associated with the provided id. </summary>
export function get(id: string) : zone.Zone {
platform.initialize();
if (id === "node") {
return new node.NodeZone();
}
@ -30,6 +31,7 @@ export function get(id: string) : zone.Zone {
export declare let current: zone.Zone;
Object.defineProperty(exports, "current", {
get: function () : zone.Zone {
platform.initialize();
if (typeof __in_napa !== 'undefined') {
return new napa.NapaZone(binding.getCurrentZone());
}

Просмотреть файл

@ -1,5 +1,6 @@
#include "napa-binding.h"
#include "napa-wraps/metric-wrap.h"
#include "napa-wraps/allocator-debugger-wrap.h"
#include "napa-wraps/allocator-wrap.h"
#include "napa-wraps/shared-ptr-wrap.h"
@ -11,7 +12,8 @@
#include <napa-memory.h>
#include <napa/module/binding/wraps.h>
#include <napa/module/worker-context.h>
#include <napa/providers/logging.h>
#include <napa/providers/metric.h>
using namespace napa;
using namespace napa::module;
@ -133,12 +135,59 @@ static void GetDefaultAllocator(const v8::FunctionCallbackInfo<v8::Value>& args)
args.GetReturnValue().Set(binding::CreateAllocatorWrap(NAPA_MAKE_SHARED<napa::memory::DefaultAllocator>()));
}
static void Log(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
CHECK_ARG(isolate, args.Length() == 4, "log accepts exactly 4 arguments (level, section, traceId, message)");
CHECK_ARG(isolate, args[0]->IsUint32(), "'level' must be a uint32 type that represents the native enum");
CHECK_ARG(isolate, args[1]->IsString() || args[1]->IsUndefined(), "'section' must be a valid string or undefined");
auto level = static_cast<napa::providers::LoggingProvider::Verboseness>(args[0]->Uint32Value());
napa::v8_helpers::Utf8String sectionValue;
const char* section = "";
if (!args[1]->IsUndefined()) {
sectionValue = napa::v8_helpers::V8ValueTo<napa::v8_helpers::Utf8String>(args[1]);
section = sectionValue.Length() > 0 ? sectionValue.Data() : "";
}
auto& logger = napa::providers::GetLoggingProvider();
// If log is not enabled we can return early.
if (!logger.IsLogEnabled(section, level)) {
return;
}
CHECK_ARG(isolate, args[2]->IsString() || args[2]->IsUndefined(), "'traceId' must be a valid string or undefined");
CHECK_ARG(isolate, args[3]->IsString(), "'message' must be a valid string");
napa::v8_helpers::Utf8String traceIdValue;
const char* traceId = "";
if (!args[2]->IsUndefined()) {
traceIdValue = napa::v8_helpers::V8ValueTo<napa::v8_helpers::Utf8String>(args[2]);
traceId = traceIdValue.Length() > 0 ? traceIdValue.Data() : "";
}
v8::String::Utf8Value message(args[3]->ToString());
// Get the first frame in user code.
// The first 2 frames are part of the log.js file.
auto stackFrame = v8::StackTrace::CurrentStackTrace(isolate, 3)->GetFrame(2);
v8::String::Utf8Value file(stackFrame->GetScriptName());
int line = stackFrame->GetLineNumber();
logger.LogMessage(section, level, traceId, *file, line, *message);
}
void binding::Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
// Register napa binding in worker context.
RegisterBinding(module);
AllocatorDebuggerWrap::Init();
AllocatorWrap::Init();
MetricWrap::Init();
SharedPtrWrap::Init();
StoreWrap::Init();
TransportContextWrapImpl::Init();
@ -146,6 +195,7 @@ void binding::Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module)
NAPA_EXPORT_OBJECTWRAP(exports, "AllocatorDebuggerWrap", AllocatorDebuggerWrap);
NAPA_EXPORT_OBJECTWRAP(exports, "AllocatorWrap", AllocatorWrap);
NAPA_EXPORT_OBJECTWRAP(exports, "MetricWrap", MetricWrap);
NAPA_EXPORT_OBJECTWRAP(exports, "SharedPtrWrap", SharedPtrWrap);
NAPA_EXPORT_OBJECTWRAP(exports, "TransportContextWrap", TransportContextWrapImpl);
@ -160,4 +210,6 @@ void binding::Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module)
NAPA_SET_METHOD(exports, "getCrtAllocator", GetCrtAllocator);
NAPA_SET_METHOD(exports, "getDefaultAllocator", GetDefaultAllocator);
NAPA_SET_METHOD(exports, "log", Log);
}

Просмотреть файл

@ -0,0 +1,122 @@
#include "metric-wrap.h"
using namespace napa::module;
using namespace napa::v8_helpers;
NAPA_DEFINE_PERSISTENT_CONSTRUCTOR(MetricWrap);
void MetricWrap::Init() {
auto isolate = v8::Isolate::GetCurrent();
// Prepare constructor template.
auto functionTemplate = v8::FunctionTemplate::New(isolate, ConstructorCallback);
functionTemplate->SetClassName(MakeV8String(isolate, _exportName));
functionTemplate->InstanceTemplate()->SetInternalFieldCount(1);
// Prototypes.
NAPA_SET_PROTOTYPE_METHOD(functionTemplate, "set", Set);
NAPA_SET_PROTOTYPE_METHOD(functionTemplate, "increment", Increment);
NAPA_SET_PROTOTYPE_METHOD(functionTemplate, "decrement", Decrement);
// Set persistent constructor into V8.
NAPA_SET_PERSISTENT_CONSTRUCTOR(_exportName, functionTemplate->GetFunction());
}
MetricWrap::MetricWrap(napa::providers::Metric* metric, uint32_t dimensions) :
_metric(metric), _dimensions(dimensions) {
}
void MetricWrap::ConstructorCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
JS_ENSURE(isolate, args.IsConstructCall(), "class \"MetricWrap\" allows constructor call only.");
CHECK_ARG(isolate, args.Length() == 4,
"class \"MetricWrap\" accepts exactly 4 arguments (section, name, type, dimensions)");
CHECK_ARG(isolate, args[0]->IsString(), "'section' must be a valid string");
CHECK_ARG(isolate, args[1]->IsString(), "'name' must be a valid string");
CHECK_ARG(isolate, args[2]->IsUint32(), "'type' must be a uint32 type that represents the native enum");
CHECK_ARG(isolate, args[3]->IsArray(), "'dimensions' must be a valid array");
auto section = V8ValueTo<Utf8String>(args[0]);
auto name = V8ValueTo<Utf8String>(args[1]);
auto type = static_cast<napa::providers::MetricType>(args[2]->Uint32Value());
// Holds te dimensions strings on the stack for the GetMetric call.
auto dimensionsStringsHolder = V8ArrayToVector<Utf8String>(isolate, v8::Local<v8::Array>::Cast(args[3]));
std::vector<const char*> dimensions;
dimensions.reserve(dimensionsStringsHolder.size());
for (const auto& dimension : dimensionsStringsHolder) {
dimensions.emplace_back(dimension.Data());
}
auto& metricProvider = napa::providers::GetMetricProvider();
auto metric = metricProvider.GetMetric(section.Data(), name.Data(), type, dimensions.size(), dimensions.data());
auto wrap = new MetricWrap(metric, static_cast<uint32_t>(dimensions.size()));
wrap->Wrap(args.This());
args.GetReturnValue().Set(args.This());
}
void MetricWrap::Set(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
CHECK_ARG(isolate, args[0]->IsUint32(), "'value' argument must be a valid Uint32");
auto value = args[0]->Uint32Value();
InvokeWithDimensions(args, 1, [value](napa::providers::Metric* metric, std::vector<const char*>& dimensions) {
metric->Set(value, dimensions.size(), dimensions.data());
});
}
void MetricWrap::Increment(const v8::FunctionCallbackInfo<v8::Value>& args) {
InvokeWithDimensions(args, 0, [](napa::providers::Metric* metric, std::vector<const char*>& dimensions) {
metric->Increment(1, dimensions.size(), dimensions.data());
});
}
void MetricWrap::Decrement(const v8::FunctionCallbackInfo<v8::Value>& args) {
InvokeWithDimensions(args, 0, [](napa::providers::Metric* metric, std::vector<const char*>& dimensions) {
metric->Decrement(1, dimensions.size(), dimensions.data());
});
}
template <typename Func>
void MetricWrap::InvokeWithDimensions(const v8::FunctionCallbackInfo<v8::Value>& args, uint32_t index, Func&& func) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto dimensionsArg = args[index];
CHECK_ARG(isolate, dimensionsArg->IsArray() || dimensionsArg->IsUndefined(), "'dimensions' must be an array or undefined");
auto wrap = NAPA_OBJECTWRAP::Unwrap<MetricWrap>(args.Holder());
// Holds the dimensions strings on the stack for so it exists during the call to func.
std::vector<napa::v8_helpers::Utf8String> dimensionsStringsHolder;
std::vector<const char*> dimensions;
if (dimensionsArg->IsArray() && wrap->_dimensions > 0) {
auto arr = v8::Local<v8::Array>::Cast(dimensionsArg);
JS_ENSURE(
isolate,
wrap->_dimensions == arr->Length(),
"the dimensions count does not match. expected: %d, received: %d",
wrap->_dimensions,
arr->Length());
dimensionsStringsHolder = napa::v8_helpers::V8ArrayToVector<napa::v8_helpers::Utf8String>(isolate, arr);
dimensions.reserve(dimensionsStringsHolder.size());
for (const auto& dimension : dimensionsStringsHolder) {
dimensions.emplace_back(dimension.Data());
}
} else {
JS_ENSURE(isolate, wrap->_dimensions == 0, "expected %s dimensions but received 0", wrap->_dimensions);
}
func(wrap->_metric, dimensions);
}

Просмотреть файл

@ -0,0 +1,62 @@
#pragma once
#include <napa-module.h>
#include <napa/module/common.h>
#include <napa/providers/metric.h>
#include <memory>
namespace napa {
namespace module {
/// <summary> An object wrap to expose metric APIs. </summary>
class MetricWrap : public NAPA_OBJECTWRAP {
public:
/// <summary> Initializes the wrap. </summary>
static void Init();
/// <summary> Create a new MetricWrap instance that wraps the provided metric. </summary>
static v8::Local<v8::Object> NewInstance(napa::providers::Metric* metric, uint32_t dimensions);
/// <summary> Declare persistent constructor to create Metric Javascript wrapper instance. </summary>
NAPA_DECLARE_PERSISTENT_CONSTRUCTOR;
private:
/// <summary> The underlying metric. </summary>
napa::providers::Metric* _metric;
/// <summary> Exported class name. </summary>
static constexpr const char* _exportName = "MetricWrap";
/// <summary> Number of dimensions this metric expects. </summary>
uint32_t _dimensions;
/// <summary> Constructor. </summary>
MetricWrap(napa::providers::Metric* metric, uint32_t dimensions);
/// <summary> MetricWrap.constructor </summary>
static void ConstructorCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
// MetricWrap getters
static void NameGetter(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>& args);
static void SectionGetter(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>& args);
// MetricWrap methods
static void Set(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Increment(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Decrement(const v8::FunctionCallbackInfo<v8::Value>& args);
/// <summary>
/// Helper method that extracts the dimensions and metric from args and calls the func
/// with these arguments.
/// </summary>
template <typename Func>
static void InvokeWithDimensions(const v8::FunctionCallbackInfo<v8::Value>& args, uint32_t index, Func&& func);
};
}
}

Просмотреть файл

@ -2,6 +2,7 @@
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)\allocator-debugger-wrap.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\allocator-wrap.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\metric-wrap.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\shared-ptr-wrap.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\store-wrap.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\transport-context-wrap-impl.cpp" />

Просмотреть файл

@ -18,7 +18,11 @@ namespace providers {
const char* file,
int line,
const char* message) override {
printf("[%s] %s [%s:%d]\n", section, message, file, line);
if (section == nullptr || section[0] == '\0') {
printf("%s [%s:%d]\n", message, file, line);
} else {
printf("[%s] %s [%s:%d]\n", section, message, file, line);
}
}
virtual bool IsLogEnabled(const char* section, Verboseness level) override {

Просмотреть файл

@ -6,7 +6,7 @@ namespace napa {
namespace providers {
///<summary> A no-operation instance of a Metric. </summary>
class NoOpMetric : public Metric {
class NopMetric : public Metric {
public:
bool Set(int64_t, size_t, const char*[]) override {
@ -31,7 +31,7 @@ namespace providers {
{
public:
NopMetricProvider() : _defaultMetric(new NoOpMetric()) {}
NopMetricProvider() : _defaultMetric(new NopMetric()) {}
Metric* GetMetric(const char*, const char*, MetricType, size_t, const char**) override {
return _defaultMetric;

Просмотреть файл

@ -4,11 +4,13 @@
#include "nop-logging-provider.h"
#include "nop-metric-provider.h"
#include "module/module-resolver.h"
#include <napa-log.h>
#include <boost/dll.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
#include <string>
@ -18,19 +20,14 @@ using namespace napa::providers;
static LoggingProvider* LoadLoggingProvider(const std::string& providerName);
static MetricProvider* LoadMetricProvider(const std::string& providerName);
// Providers.
static LoggingProvider* _loggingProvider = nullptr;
static MetricProvider* _metricProvider = nullptr;
// Providers - Initilally assigned to defaults.
static LoggingProvider* _loggingProvider = LoadLoggingProvider("");
static MetricProvider* _metricProvider = LoadMetricProvider("");
bool napa::providers::Initialize(const napa::PlatformSettings& settings) {
try {
_loggingProvider = LoadLoggingProvider(settings.loggingProvider);
_metricProvider = LoadMetricProvider(settings.metricProvider);
} catch (std::exception& ex) {
std::cerr << "Error occurred while loading providers: " << ex.what() << "\n";
return false;
}
_loggingProvider = LoadLoggingProvider(settings.loggingProvider);
_metricProvider = LoadMetricProvider(settings.metricProvider);
return true;
}
@ -46,38 +43,67 @@ void napa::providers::Shutdown() {
}
LoggingProvider& napa::providers::GetLoggingProvider() {
NAPA_ASSERT(_loggingProvider, "logging provider not set");
return *_loggingProvider;
}
MetricProvider& napa::providers::GetMetricProvider() {
NAPA_ASSERT(_metricProvider, "metric provider not set");
return *_metricProvider;
}
template <typename ProviderType>
static ProviderType* LoadProvider(const std::string& providerName, const std::string& functionName) {
// TODO @asib: resolve path to shared library given the provider name, need to use module loader APIs.
auto createProviderFunc = boost::dll::import<ProviderType*()>(providerName, functionName);
static ProviderType* LoadProvider(
const std::string& providerName,
const std::string& jsonProperyPath,
const std::string& functionName) {
napa::module::ModuleResolver moduleResolver;
// Resolve the provider module information
auto moduleInfo = moduleResolver.Resolve(providerName.c_str());
NAPA_ASSERT(!moduleInfo.packageJsonPath.empty(), "missing package.json in provider '%s'", providerName.c_str());
// Full path to the root of the provider module
auto modulePath = boost::filesystem::path(moduleInfo.packageJsonPath).parent_path();
// Extract relative path to provider dll from package.json
boost::property_tree::ptree package;
boost::property_tree::json_parser::read_json(moduleInfo.packageJsonPath, package);
auto providerRelativePath = package.get_optional<std::string>(jsonProperyPath);
NAPA_ASSERT(
providerRelativePath.is_initialized(),
"missing property '%s' in '%s'",
jsonProperyPath.c_str(),
moduleInfo.packageJsonPath.c_str());
// Full path to provider dll
auto providerPath = (modulePath / providerRelativePath.get()).normalize().make_preferred();
// boost::dll unloads dll when a reference object is gone.
// Keep a static instance for each provider type (each template type will have its own static variable).
static auto createProviderFunc = boost::dll::import<ProviderType*()>(providerPath, functionName);
return createProviderFunc();
}
static LoggingProvider* LoadLoggingProvider(const std::string& providerName) {
if (providerName.empty() || providerName == "nop") {
return new NopLoggingProvider();
if (providerName.empty() || providerName == "console") {
static auto consoleLoggingProvider = std::make_unique<ConsoleLoggingProvider>();
return consoleLoggingProvider.get();
}
if (providerName == "console") {
return new ConsoleLoggingProvider();
if (providerName == "nop") {
static auto nopLoggingProvider = std::make_unique<NopLoggingProvider>();
return nopLoggingProvider.get();
}
return LoadProvider<LoggingProvider>(providerName, "CreateLoggingProvider");
return LoadProvider<LoggingProvider>(providerName, "providers.logging", "CreateLoggingProvider");
}
static MetricProvider* LoadMetricProvider(const std::string& providerName) {
if (providerName.empty()) {
return new NopMetricProvider();
static auto nopMetricProvider = std::make_unique<NopMetricProvider>();
return nopMetricProvider.get();
}
return LoadProvider<MetricProvider>(providerName, "CreateMetricProvider");;
return LoadProvider<MetricProvider>(providerName, "providers.metric", "CreateMetricProvider");;
}

Просмотреть файл

@ -14,11 +14,5 @@ namespace providers {
/// <summary> Clean up and destroy all loaded providers. </summary>
void Shutdown();
/// <summary> Returns the logging provider. </summary>
LoggingProvider& GetLoggingProvider();
/// <summary> Returns the metric provider. </summary>
MetricProvider& GetMetricProvider();
}
}

Просмотреть файл

@ -8,6 +8,7 @@
<ItemGroup>
<QCustomProjectReference Include="$(NapaVanillaRoot)\package\napajs.proj" />
<QCustomProjectReference Include="test-provider\package\test-provider.proj" />
</ItemGroup>
<ItemGroup>
<QCustomInput Include="$(NapaBuildRoot)\**\*" />
@ -38,6 +39,12 @@
</ItemGroup>
<Copy SourceFiles="@(RuntimeFiles)" DestinationFiles="@(RuntimeFiles->'$(PackageOutPath)\%(RecursiveDir)%(Filename)%(Extension)')" ContinueOnError="false" SkipUnchangedFiles="true" />
<Message Text="Prepare 'test-provider'' runtime files." />
<ItemGroup>
<TestProviderFiles Include="test-provider\package\$(IntermediateOutputPath)\node_modules\test-provider\**\*.*"/>
</ItemGroup>
<Copy SourceFiles="@(TestProviderFiles)" DestinationFiles="@(TestProviderFiles->'$(IntermediateOutputPath)\node_modules\test-provider\%(RecursiveDir)%(Filename)%(Extension)')" ContinueOnError="false" SkipUnchangedFiles="true" />
</Target>
<Import Project="$(ExtendedTargetsPath)\NoTarget.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,137 @@
import * as napa from "napajs";
import * as assert from "assert";
import * as path from "path";
// Require this module to verify logging was done correctly
var testProvider = require('test-provider');
napa.runtime.setPlatformSettings({
loggingProvider: "test-provider",
metricProvider: "test-provider"
});
let zone: napa.zone.Zone = napa.zone.create('napa-zone1', { workers: 1});
describe('napajs/logging', function () {
it('@node: default log', () => {
napa.log("test");
assert.strictEqual(testProvider.lastLog.message, 'test');
assert.strictEqual(testProvider.lastLog.level, 2 /* LogLevel.Info */);
});
it('@napa: default log', () => {
zone.broadcastSync(() => {
var napa = require('napajs');
napa.log("test-from-napa");
}, []);
assert.strictEqual(testProvider.lastLog.message, 'test-from-napa');
assert.strictEqual(testProvider.lastLog.level, 2 /* LogLevel.Info */);
});
it('@node: log with section', () => {
napa.log("test-section", "test");
assert.strictEqual(testProvider.lastLog.message, 'test');
assert.strictEqual(testProvider.lastLog.section, "test-section");
});
it('@napa: log with section', () => {
zone.broadcastSync(() => {
var napa = require('napajs');
napa.log("test-section", "test-from-napa");
}, []);
assert.strictEqual(testProvider.lastLog.message, 'test-from-napa');
assert.strictEqual(testProvider.lastLog.section, "test-section");
});
it('@node: log with section and trace id', () => {
napa.log("test-section", "trace-id", "test");
assert.strictEqual(testProvider.lastLog.message, 'test');
assert.strictEqual(testProvider.lastLog.section, "test-section");
assert.strictEqual(testProvider.lastLog.traceId, 'trace-id');
});
it('@napa: log with section and trace id', () => {
zone.broadcastSync(() => {
var napa = require('napajs');
napa.log("test-section", "trace-id", "test-from-napa");
}, []);
assert.strictEqual(testProvider.lastLog.message, 'test-from-napa');
assert.strictEqual(testProvider.lastLog.section, "test-section");
assert.strictEqual(testProvider.lastLog.traceId, 'trace-id');
});
it('@node: log with verbosity', () => {
napa.log.err("test0");
assert.strictEqual(testProvider.lastLog.message, 'test0');
assert.strictEqual(testProvider.lastLog.level, 0 /* LogLevel.Error */);
napa.log.warn("test1");
assert.strictEqual(testProvider.lastLog.message, 'test1');
assert.strictEqual(testProvider.lastLog.level, 1 /* LogLevel.Warning */);
napa.log.info("test2");
assert.strictEqual(testProvider.lastLog.message, 'test2');
assert.strictEqual(testProvider.lastLog.level, 2 /* LogLevel.Information */);
napa.log.debug("test3");
assert.strictEqual(testProvider.lastLog.message, 'test3');
assert.strictEqual(testProvider.lastLog.level, 3 /* LogLevel.Debug */);
});
it('@napa: log with verbosity', () => {
zone.broadcastSync(() => { var napa = require('napajs'); napa.log.err("test-from-napa0"); }, []);
assert.strictEqual(testProvider.lastLog.message, 'test-from-napa0');
assert.strictEqual(testProvider.lastLog.level, 0 /* LogLevel.Error */);
zone.broadcastSync(() => { var napa = require('napajs'); napa.log.warn("test-from-napa1"); }, []);
assert.strictEqual(testProvider.lastLog.message, 'test-from-napa1');
assert.strictEqual(testProvider.lastLog.level, 1 /* LogLevel.Warning */);
zone.broadcastSync(() => { var napa = require('napajs'); napa.log.info("test-from-napa2"); }, []);
assert.strictEqual(testProvider.lastLog.message, 'test-from-napa2');
assert.strictEqual(testProvider.lastLog.level, 2 /* LogLevel.Information */);
zone.broadcastSync(() => { var napa = require('napajs'); napa.log.debug("test-from-napa3"); }, []);
assert.strictEqual(testProvider.lastLog.message, 'test-from-napa3');
assert.strictEqual(testProvider.lastLog.level, 3 /* LogLevel.Debug */);
});
});
describe('napajs/metric', function () {
it('@node: get metric', () => {
let metric = napa.metric.get("section1", "name1", napa.metric.MetricType.Number);
assert.strictEqual(metric.section, 'section1');
assert.strictEqual(metric.name, 'name1');
});
it('@napa: get metric', () => {
zone.broadcastSync(() => {
var napa = require('napajs');
var metric = napa.metric.get("section2", "name2", napa.metric.MetricType.Number) ;
metric.set(24);
}, []);
assert.strictEqual(testProvider.lastMetricValue, 24);
});
it('@node: get metric from cache (per isolate)', () => {
let metric = napa.metric.get("section3", "name3", napa.metric.MetricType.Number);
metric.set(7);
metric = napa.metric.get("section3", "name3", napa.metric.MetricType.Number);
assert.strictEqual(testProvider.lastMetricValue, 7);
});
it('@napa: get metric from cache (per isolate)', () => {
zone.broadcastSync(() => {
var napa = require('napajs');
let metric = napa.metric.get("section4", "name4", napa.metric.MetricType.Number);
metric.set(15);
metric = napa.metric.get("section4", "name4", napa.metric.MetricType.Number);
metric.increment();
}, []);
assert.strictEqual(testProvider.lastMetricValue, 16);
});
});

Просмотреть файл

@ -0,0 +1,6 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AddonType>Napa</AddonType>
</PropertyGroup>
<Import Project="..\node\addon.vcxproj" />
</Project>

Просмотреть файл

@ -0,0 +1,46 @@
#include <napa-module.h>
#include <napa/v8-helpers.h>
#include "logging-provider.h"
#include "metric-provider.h"
#include <string>
using namespace napa::v8_helpers;
void LastLogGetter(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
auto& lastLog = TestLoggingProvider::GetInstance().GetLastLog();
auto obj = v8::Object::New(isolate);
obj->CreateDataProperty(context, MakeV8String(isolate, "section"), MakeV8String(isolate, lastLog.section));
obj->CreateDataProperty(context, MakeV8String(isolate, "message"), MakeV8String(isolate, lastLog.message));
obj->CreateDataProperty(context, MakeV8String(isolate, "traceId"), MakeV8String(isolate, lastLog.traceId));
obj->CreateDataProperty(context, MakeV8String(isolate, "file"), MakeV8String(isolate, lastLog.file));
obj->CreateDataProperty(context, MakeV8String(isolate, "line"), v8::Integer::New(isolate, lastLog.line));
auto level = static_cast<uint32_t>(lastLog.level);
obj->CreateDataProperty(context, MakeV8String(isolate, "level"),v8::Uint32::NewFromUnsigned(isolate, level));
args.GetReturnValue().Set(obj);
}
static void LastMetricValueGetter(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto value = TestMetricProvider::GetInstance().GetLastMetric()->GetValue();
args.GetReturnValue().Set(static_cast<int32_t>(value));
}
void InitAll(v8::Local<v8::Object> exports) {
exports->SetAccessor(MakeV8String(v8::Isolate::GetCurrent(), "lastLog"), LastLogGetter);
exports->SetAccessor(MakeV8String(v8::Isolate::GetCurrent(), "lastMetricValue"), LastMetricValueGetter);
}
NAPA_MODULE(addon, InitAll);

Просмотреть файл

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(EnvironmentConfig)" />
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.Default.props" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup>
<AddonName>addon</AddonName>
<TargetName Condition=" '$(AddonType)' != 'Napa' ">$(AddonName)</TargetName>
<TargetName Condition=" '$(AddonType)' == 'Napa' ">$(AddonName)_napa</TargetName>
</PropertyGroup>
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.props" />
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions Condition=" '$(AddonType)' != 'Napa' ">BUILDING_NODE_EXTENSION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition=" '$(AddonType)' == 'Napa' ">BUILDING_NAPA_EXTENSION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(NapaVanillaRoot)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\test-provider.vcxproj" />
<ProjectReference Include="$(NapaVanillaRoot)\src\napa.vcxproj" Condition=" '$(AddonType)' == 'Napa' " />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)\addon.cpp" />
</ItemGroup>
<Target Name="ChangeExtention" AfterTargets="Build">
<Move SourceFiles="$(OutputPath)\$(TargetFileName)" DestinationFiles="$(OutputPath)\$(AddonName).node" Condition=" '$(AddonType)' != 'Napa' " />
<Move SourceFiles="$(OutputPath)\$(TargetFileName)" DestinationFiles="$(OutputPath)\$(AddonName).napa" Condition=" '$(AddonType)' == 'Napa' " />
</Target>
<Import Project="$(Pkgnapa_nodelib_vc140)\exports_node.props" Condition=" '$(AddonType)' != 'Napa' " />
<Import Project="$(Pkgnapa_nodelib_vc140)\exports_v8_includes.props" Condition=" '$(AddonType)' == 'Napa' " />
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,10 @@
{
"name": "test-provider",
"version": "0.1.0",
"author": "napajs",
"main": "./bin/addon",
"providers": {
"logging": "./bin/test-provider.dll",
"metric": "./bin/test-provider.dll"
}
}

Просмотреть файл

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(EnvironmentConfig)" />
<!-- These targets must be built before the package -->
<ItemGroup>
<QCustomProjectReference Include="..\src\test-provider.vcxproj" />
<QCustomProjectReference Include="..\napa\addon.vcxproj" />
<QCustomProjectReference Include="..\node\addon.vcxproj" />
</ItemGroup>
<PropertyGroup>
<PackageOutPath>$(IntermediateOutputPath)\node_modules\test-provider</PackageOutPath>
</PropertyGroup>
<Target Name="CreateNpmPackage" AfterTargets="AfterBuild">
<Message Text="Create NPM package." />
<!-- Setup package root directory -->
<Copy SourceFiles="..\package.json" DestinationFolder="$(PackageOutPath)" ContinueOnError="false" SkipUnchangedFiles="true" />
<!-- Setup ./bin directory-->
<!-- Binplace napa binaries -->
<Copy SourceFiles="..\src\$(IntermediateOutputPath)\test-provider.dll" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="..\napa\$(IntermediateOutputPath)\addon.napa" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
<Copy SourceFiles="..\node\$(IntermediateOutputPath)\addon.node" DestinationFolder="$(PackageOutPath)\bin" ContinueOnError="false" SkipUnchangedFiles="true" />
</Target>
<Import Project="$(ExtendedTargetsPath)\NoTarget.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,37 @@
#include "logging-provider.h"
using namespace napa::providers;
TestLoggingProvider& TestLoggingProvider::GetInstance() {
static TestLoggingProvider instance;
return instance;
}
const LogEntry& TestLoggingProvider::GetLastLog() const {
return _lastLog;
}
void TestLoggingProvider::LogMessage(
const char* section,
Verboseness level,
const char* traceId,
const char* file,
int line,
const char* message) {
_lastLog = { section, level, traceId, file, line, message };
}
bool TestLoggingProvider::IsLogEnabled(const char *, Verboseness) {
return true;
}
void TestLoggingProvider::Destroy() {
// Singleton
}
// Export a function for creating the test logging provider
EXTERN_C NAPA_API LoggingProvider* CreateLoggingProvider() {
return &TestLoggingProvider::GetInstance();
}

Просмотреть файл

@ -0,0 +1,40 @@
#pragma once
#include <napa/providers/logging.h>
#include <string>
struct LogEntry {
std::string section;
napa::providers::LoggingProvider::Verboseness level;
std::string traceId;
std::string file;
int line;
std::string message;
};
class TestLoggingProvider : public napa::providers::LoggingProvider {
public:
NAPA_API static TestLoggingProvider& GetInstance();
NAPA_API const LogEntry& GetLastLog() const;
void LogMessage(
const char* section,
Verboseness level,
const char* traceId,
const char* file,
int line,
const char* message) override;
bool IsLogEnabled(const char *, Verboseness) override;
void Destroy() override;
private:
LogEntry _lastLog;
TestLoggingProvider() = default;
};

Просмотреть файл

@ -0,0 +1,54 @@
#include "metric-provider.h"
using namespace napa::providers;
TestMetric::TestMetric() : _value(0) {
}
bool TestMetric::Set(int64_t value, size_t, const char*[]) {
_value = value;
return true;
}
bool TestMetric::Increment(uint64_t value, size_t, const char*[]) {
_value += value;
return true;
}
bool TestMetric::Decrement(uint64_t value, size_t, const char*[]) {
_value -= value;
return true;
}
void TestMetric::Destroy() {
delete this;
}
int64_t TestMetric::GetValue() const {
return _value;
}
TestMetricProvider& TestMetricProvider::GetInstance() {
static TestMetricProvider instance;
return instance;
}
Metric* TestMetricProvider::GetMetric(const char*, const char*, MetricType, size_t, const char*[]) {
_metrics.emplace_back(TestMetric());
return &_metrics.back();
}
const TestMetric* TestMetricProvider::GetLastMetric() const {
return &_metrics.back();
}
void TestMetricProvider::Destroy() {
// Singleton
}
// Export a function for creating the test metric provider
EXTERN_C NAPA_API MetricProvider* CreateMetricProvider() {
return &TestMetricProvider::GetInstance();
}

Просмотреть файл

@ -0,0 +1,47 @@
#pragma once
#include <napa/providers/metric.h>
#include <string>
#include <vector>
class TestMetric : public napa::providers::Metric {
public:
explicit TestMetric();
bool Set(int64_t value, size_t, const char*[]) override;
bool Increment(uint64_t value, size_t, const char*[]) override;
bool Decrement(uint64_t value, size_t, const char*[]) override;
void Destroy() override;
NAPA_API int64_t GetValue() const;
private:
int64_t _value;
};
class TestMetricProvider : public napa::providers::MetricProvider {
public:
NAPA_API static TestMetricProvider& GetInstance();
NAPA_API const TestMetric* GetLastMetric() const;
napa::providers::Metric* GetMetric(
const char* section,
const char* name,
napa::providers::MetricType type,
size_t dimensions,
const char* dimensionNames[]) override;
void Destroy() override;
private:
std::vector<TestMetric> _metrics;
TestMetricProvider() = default;
};

Просмотреть файл

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(EnvironmentConfig)" />
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.Default.props" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.props" />
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>NAPA_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(NapaVanillaRoot)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="logging-provider.cpp" />
<ClCompile Include="metric-provider.cpp" />
</ItemGroup>
<Import Project="$(ExtendedTargetsPath)\Microsoft.Cpp.targets" />
</Project>