Adding logging to a component

Key takeaways

Logging Logging is the capacity to create tagged messages from events in our code.
Logging levels Logging levels: nothing, fatal, error, warn, info, debug, and trace.
ConsoleLogger PIP.Services component for displaying logging messages on the console.
CachedLogger PIP.Services component that caches log messages in memory.
CompositeLogger PIP.Services component for aggregating logging messages.
DataDogLogger, ElasticSearchLogger, CloudWatchLogger PIP.Services logger implementations for Datadog, Elasticsearch, and Amazon CloudWatch components.

Introduction

In this tutorial, you will learn how to add logging capacity to a microservice. First, we will understand what logging consists of. Then, we will use the microservice we created in the “Creating a component” tutorial, replace the printed messages with logger messages and create an exception in our business process (my_task). After running the code, we will see the tagged messages from the logger.

Once we have seen how to create a logger that displays messages on our console, we will learn how to create a composite logger, which will add the capacity to aggregate the log messages from different sources and centralize their display on our console.

Finally, we will see how to add loggers for Datadog, Elasticsearch, and Amazon CloudWatch components.

What is logging?

Logging is the capacity to create tagged messages from events in our code. These messages can inform us about the running process.

There are different logging levels. PIP.Services defines them as:

Level name Level number Description
Nothing 0 Nothing to be logged.
Fatal 1 Logs only fatal errors that cause a microservice to fail.
Error 2 Logs all errors - fatal or recoverable.
Warn 3 Logs errors and warnings.
Info 4 Logs errors and important information messages.
Debug 5 Logs everything up to high-level debugging information.
Trace 6 Logs everything down to fine-granular debugging messages.

Once generated, log messages need to be stored or displayed. PIP.Services provides specific tools for this: CachedLogger and ConsoleLogger. The first class stores log messages in memory. The second class displays them on a console. The toolkit also provides us with the CompositeLogger, which allows for message aggregation and thus, creating a centralized logging point.

Additionally, PIP.Services provides implementations of loggers for CloudWatch, ElasticSearch, and DataDog.

Now, we will see how to create a console logger and a composite logger.

Adding a console logger to our component

In our example, we will add a logger that sends messages to our console. For this, we will use the ConsoleLogger class. After we created an instance of this class, we will set the logging level to five, which will allow us to log everything up to debug level.

var logger = new ConsoleLogger();
logger.setLevel(5);

// Logger setting
var logger = new ConsoleLogger();
logger.Level = LogLevel.Debug;

// Logger setting
logger := *clog.NewConsoleLogger()
logger.SetLevel(5)

var logger = ConsoleLogger();
logger.setLevel(LogLevel.Debug);

# Logger setting
logger = ConsoleLogger()
logger.set_level(5) 

Not available

Then, we will replace our print messages with info-level log messages. For example, print(“MyComponentA has been created.") will be replaced with logger.info(None, “MyComponentA has been created.").

Finally, we will force an exception in the my_task method. As we had explained in the “Creating a component” tutorial, this method performs business-related tasks. Thus, we can simulate a problem within it by forcibly raising an exception. This method will look like this:

public myTask(correlationId: string): void {
    // create an artificial problem
    try{
        throw Error('Logging demo. Error created');
    }
    catch (ex) {
        logger.error(correlationId, ex, "Error created.")  
    }
        
}

public void MyTask(string correlationId)
{
    var logger = new ConsoleLogger();
    logger.Level = LogLevel.Debug;
    // create an artificial problem
    try
    {
        throw new Exception("Logging demo. Error created");
    }
    catch (Exception e)
    {
        logger.Error(correlationId, e, "Error created.");
    }
}

func MyTask(correlationId string) {
	// create an artificial problem
	err := errors.New("Logging demo. Error created")
	logger.Error(correlationId, err, "Error created.")
}

void myTask(String? correlationId) {
  // create an artificial problem
  try {
    throw Exception('Logging demo. Error created');
  } catch (ex) {
    logger.error(correlationId, ex as Exception, 'Error created.');
  }
}

def my_task(self, correlation_id):
    # create an artificial problem        
    try:
        raise Exception('Logging demo', 'Error created')
    except Exception as inst:
        logger.error(correlation_id, inst, "Error created.")  
Not available

And, our final code will look like this:

a) Our components

import { ConfigParams, Descriptor, ICleanable, IOpenable, IUnreferenceable } from 'pip-services3-commons-nodex';
import { IConfigurable } from 'pip-services3-commons-nodex';
import { IReferences } from 'pip-services3-commons-nodex';
import { IReferenceable } from 'pip-services3-commons-nodex';
import { CompositeLogger, ConsoleLogger, Factory } from 'pip-services3-components-nodex';

export class MyComponentB implements IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable {
    private _param1: string = 'ABC2';
    private _param2: number = 456;
    private _opened:boolean = false;

    private _status: string;

    private _logger = new ConsoleLogger();
     

    /**
     * Creates a new instance of the component.
     */
    public constructor(){
        this._status = 'Created';
        this._logger.setLevel(5);
        this._logger.info(null, "MyComponentB has been configured.");

    }
    
    public configure(config: ConfigParams): void {
        this._param1 = config.getAsStringWithDefault('param1', this._param1);
        this._param2 = config.getAsIntegerWithDefault('param2', this._param2);

        this._logger.info(null, "MyComponentB has been configured.")
    }

    public setReferences(refs: IReferences): void {
        // pass
    }

    public isOpen(): boolean {
        // pass
    }
    public open(correlationId: string): Promise<void> {
        // pass
    }
    public close(correlationId: string): Promise<void> {
        // pass
    }

    public myTask(correlationId: string): void {
        // create an artificial problem
        try {
            throw Error('Logging demo. Error created');
        }
        catch (ex) {
            this._logger.error(correlationId, ex, "Error created.")
        }

    }

    /**
     * Unsets (clears) previously set references to dependent components.
     */
    public unsetReferences(): void {
        // pass
    }

    /**
     * Clears component state.
     * @param correlationId (optional) transaction id to trace execution through call chain.
     */
    public clear(correlationId: string): Promise<void> {
        // pass
        return;
    }
}

export class MyComponentA implements IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable {
    private _param1: string = 'ABC';
    private _param2: number = 123;
    private _opened: boolean = false;

    private _status: string;

    private _another_component: MyComponentB;

    public dummy_variable = "resource variable";

    private _logger = new ConsoleLogger();


    /**
     * Creates a new instance of the component.
     */
    public constructor() {
        this._status = 'Created';
        this._logger.setLevel(5);
        this._logger.info(null, "MyComponentA has been configured.");

    }

    public configure(config: ConfigParams): void {
        this._param1 = config.getAsStringWithDefault('param1', this._param1);
        this._param2 = config.getAsIntegerWithDefault('param2', this._param2);
        this._status = "Configured";

        this._logger.info(null, "MyComponentA has been configured.")
    }

    public setReferences(refs: IReferences): void {
        this._another_component = refs.getOneRequired(
            new Descriptor("myservice", "mycomponent-b", "*", "*", "1.0")
        )
        this._status = "Configured"
        this._logger.info(null, "MyComponentA's references have been defined.")
    }

    public isOpen(): boolean {
        return this._opened;
    }
    public open(correlationId: string): Promise<void> {
        this._opened = true;
        this._status = "Open";
        this._logger.info(correlationId, "MyComponentA has been opened.")
        // artificial problem
        this.myTask(correlationId);
    }
    public close(correlationId: string): Promise<void> {
        this._opened = false;
        this._status = "Closed";
        this._logger.info(correlationId, "MyComponentA has been closed.");
    }

    public myTask(correlationId: string): void {
        // create an artificial problem
        try {
            throw Error('Logging demo. Error created');
        }
        catch (ex) {
            this._logger.error(correlationId, ex, "Error created.")
        }

    }

    /**
     * Unsets (clears) previously set references to dependent components.
     */
    public unsetReferences(): void {
        this._another_component = null;
        this._status = "Un-referenced";
        this._logger.info(null, "References cleared");
    }

    /**
     * Clears component state.
     * @param correlationId (optional) transaction id to trace execution through call chain.
     */
    public clear(correlationId: string): Promise<void> {
        this.dummy_variable = null;
        this._status = null;
        this._logger.info(null, "Resources cleared");
    }
}
using PipServices3.Commons.Config;
using PipServices3.Commons.Refer;
using PipServices3.Commons.Run;
using PipServices3.Components.Log;
using System;

using System.Threading.Tasks;

namespace ExampleApp
{
    class MyComponentB : IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable
    {
        private string _param1 = "ABC2";
        private int _param2 = 456;
        private bool _opened = false;

        private string _status;

        private ConsoleLogger _logger = new ConsoleLogger();

        /// <summary>
        /// Creates a new instance of the component.
        /// </summary>
        public MyComponentB()
        {
            this._status = "Created";
            this._logger.Level = LogLevel.Debug;
            this._logger.Info(null, "MyComponentB has been configured.");
        }

        public void Configure(ConfigParams config)
        {
            this._param1 = config.GetAsStringWithDefault("param1", this._param1);
            this._param2 = config.GetAsIntegerWithDefault("param2", this._param2);

            this._logger.Info(null, "MyComponentB has been configured.");
        }

        public void SetReferences(IReferences references)
        {

        }

        public bool IsOpen()
        {
            return _opened;
        }

        public async Task OpenAsync(string correlationId)
        {
            await Task.FromResult(0);
        }


        public async Task CloseAsync(string correlationId)
        {
            await Task.FromResult(0);
        }

        public void MyTask(string correlationId)
        {
            var logger = new ConsoleLogger();
            logger.Level = LogLevel.Debug;
            // create an artificial problem
            try
            {
                throw new Exception("Logging demo. Error created");
            }
            catch (Exception e)
            {
                logger.Error(correlationId, e, "Error created.");
            }
        }

        /// <summary>
        /// Unsets (clears) previously set references to dependent components.
        /// </summary>
        public void UnsetReferences()
        {

        }

        /// <summary>
        /// Clears component state.
        /// </summary>
        /// <param name="correlationId">(optional) transaction id to trace execution through call chain.</param>
        /// <returns></returns>
        public async Task ClearAsync(string correlationId)
        {
            await Task.FromResult(0);
        }

    }


    class MyComponentA : IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable
    {
        private string _param1 = "ABC";
        private int _param2 = 123;
        private bool _opened = false;
        private string _status;

        private MyComponentB _another_component;

        private ConsoleLogger _logger = new ConsoleLogger();

        public string dummy_variable = "resource variable";

        /// <summary>
        /// Creates a new instance of the component.
        /// </summary>
        public MyComponentA()
        {
            this._status = "Created";
            this._logger.Level = LogLevel.Debug;
            this._logger.Info(null, "MyComponentA has been configured.");
        }

        public void Configure(ConfigParams config)
        {
            this._param1 = config.GetAsStringWithDefault("param1", this._param1);
            this._param2 = config.GetAsIntegerWithDefault("param2", this._param2);
            this._status = "Configured";

            this._logger.Info(null, "MyComponentA has been configured.");
        }

        public void SetReferences(IReferences references)
        {
            this._another_component = references.GetOneRequired(
                new Descriptor("myservice", "mycomponent-b", "*", "*", "1.0")
            ) as MyComponentB;

            this._status = "Configured";
            this._logger.Info(null, "MyComponentA's references have been defined.");
        }

        public bool IsOpen()
        {
            return this._opened;
        }

        public async Task OpenAsync(string correlationId)
        {
            this._opened = true;
            this._status = "Open";
            this._logger.Info(correlationId, "MyComponentA has been opened.");
            // artificial problem
            this.MyTask(correlationId);

            await Task.FromResult(0);
        }


        public async Task CloseAsync(string correlationId)
        {
            this._opened = false;
            this._status = "Closed";
            this._logger.Info(correlationId, "MyComponentA has been closed.");

            await Task.FromResult(0);
        }

        public void MyTask(string correlationId)
        {
            var logger = new ConsoleLogger();
            logger.Level = LogLevel.Debug;
            // create an artificial problem
            try
            {
                throw new Exception("Logging demo. Error created");
            }
            catch (Exception e)
            {
                logger.Error(correlationId, e, "Error created.");
            }
        }

        /// <summary>
        /// Unsets (clears) previously set references to dependent components.
        /// </summary>
        public void UnsetReferences()
        {
            this._another_component = null;
            this._status = "Un-referenced";
            this._logger.Info(null, "References cleared");
        }

        /// <summary>
        /// Clears component state.
        /// </summary>
        /// <param name="correlationId">(optional) transaction id to trace execution through call chain.</param>
        /// <returns></returns>
        public async Task ClearAsync(string correlationId)
        {
            this.dummy_variable = null;
            this._status = null;
            this._logger.Info(null, "Resources cleared");

            await Task.FromResult(0);
        }

    }
}


import (
	"errors"

	cconfig "github.com/pip-services3-go/pip-services3-commons-go/config"
	crefer "github.com/pip-services3-go/pip-services3-commons-go/refer"
	crun "github.com/pip-services3-go/pip-services3-commons-go/run"
	clog "github.com/pip-services3-go/pip-services3-components-go/log"
)

type MyComponentB struct {
	crefer.IReferences
	crefer.IUnreferenceable
	cconfig.IConfigurable
	crun.IOpenable
	crun.ICleanable

	_status string
	_param1 string
	_param2 int
	_opened bool

	dummy_variable interface{}
}

// Creates a new instance of the component.
func NewMyComponentB() *MyComponentB {
	component := &MyComponentB{
		_status: "Created",
		_param1: "ABC2",
		_param2: 456,
		_opened: false,
		_logger: clog.NewConsoleLogger(),

		dummy_variable: "resource variable",
	}

	component._logger.SetLevel(5)
	component._logger.Info("123", "MyComponentB has been created.")

	return component
}

func (c *MyComponentB) Configure(config *cconfig.ConfigParams) {
	c._param1 = config.GetAsStringWithDefault("param1", c._param1)
	c._param2 = config.GetAsIntegerWithDefault("param2", c._param2)

	c._logger.Info("123", "MyComponentB has been configured.")
}

func (c *MyComponentB) SetReferences(references *crefer.References) {
	// pass
}

func (c *MyComponentB) isOpen() bool {
	// pass
	return true
}

func (c *MyComponentB) Open(correlationId string) {
	// pass
}

func (c *MyComponentB) Close(correlationId string) {
	// pass
}

func (c *MyComponentB) MyTask(correlationId string) {
	// pass
}

// Unsets (clears) previously set references to dependent components.
func (c *MyComponentB) UnsetReferences() {
	// pass
}

// Clears component state.
// - correlationId: (optional) transaction id to trace execution through call chain.
func (c *MyComponentB) Clear(correlationId string) {
	// pass
}

type MyComponentA struct {
	crefer.IReferences
	crefer.IUnreferenceable
	cconfig.IConfigurable
	crun.IOpenable
	crun.ICleanable

	_logger *clog.ConsoleLogger

	_status string
	_param1 string
	_param2 int
	_opened bool

	dummy_variable interface{}

	_another_component interface{}
}

// Creates a new instance of the component.
func NewMyComponentA() *MyComponentA {
	component := &MyComponentA{
		_status: "Created",
		_param1: "ABC2",
		_param2: 456,
		_opened: false,
		_logger: clog.NewConsoleLogger(),

		dummy_variable: "dummy_variable setted",
	}

	component._logger.SetLevel(5)
	component._logger.Info(correlationId, "MyComponentA has been created.")

	return component
}

func (c *MyComponentA) Configure(config *cconfig.ConfigParams) {
	c._param1 = config.GetAsStringWithDefault("param1", c._param1)
	c._param2 = config.GetAsIntegerWithDefault("param2", c._param2)
	c._status = "Configured"

	c._logger.Info("123", "MyComponentB has been configured.")
}

func (c *MyComponentA) SetReferences(references *crefer.References) {
	_another_component, err := references.GetOneRequired(crefer.NewDescriptor("myservice", "mycomponent-b", "*", "*", "1.0"))

	if err != nil {
		panic("Error: Another Component is not in refs")
	}

	c._another_component = _another_component.(MyComponentB)
	c._status = "Configured"

	c._logger.Info("123", "MyComponentA's references have been defined.")
}

func (c *MyComponentA) isOpen() bool {
	return c._opened
}

func (c *MyComponentA) Open(correlationId string) {
	c._opened = true
	c._status = "Open"
	c._logger.Info(correlationId, "MyComponentA has been opened.")
	// artificial problem
	c.MyTask(correlationId)
}

func (c *MyComponentA) Close(correlationId string) {
	c._opened = false
	c._status = "Closed"
	c._logger.Info(correlationId, "MyComponentA has been closed.")
}

func (c *MyComponentA) MyTask(correlationId string) {
	// create an artificial problem
	c._logger.Error(correlationId, errors.New("Logging demo. Error created"), "Error created.")
}

// Unsets (clears) previously set references to dependent components.
func (c *MyComponentA) UnsetReferences() {
	c._another_component = nil
	c._status = "Un-referenced"
	c._logger.Info("123", "References cleared")
}

// Clears component state.
// - correlationId: (optional) transaction id to trace execution through call chain.
func (c *MyComponentA) Clear(correlationId string) {
	c.dummy_variable = nil
	c._status = ""
	c._logger.Info(correlationId, "Resources cleared")
}

import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_components/pip_services3_components.dart';


class MyComponentB
    implements
        IReferenceable,
        IUnreferenceable,
        IConfigurable,
        IOpenable,
        ICleanable {
  String _param1 = 'ABC2';
  int _param2 = 456;

  bool _opened = false;

  String? _status;

  var _logger = ConsoleLogger();

  /// Creates a new instance of the component.
  MyComponentB() {
    _status = 'Created';
    _logger.setLevel(LogLevel.Debug);
    _logger.info(null, 'MyComponentB has been configured.');
  }

  @override
  void configure(ConfigParams config) {
    _param1 = config.getAsStringWithDefault('param1', _param1);
    _param2 = config.getAsIntegerWithDefault('param2', _param2);
    _logger.info(null, 'MyComponentB has been configured.');
  }

  void myTask(String? correlationId) {
    // create an artificial problem
    try {
      throw Exception('Logging demo. Error created');
    } catch (ex) {
      _logger.error(correlationId, ex as Exception, 'Error created.');
    }
  }

  @override
  void setReferences(IReferences references) {
    // pass
  }

  @override
  bool isOpen() {
    // pass
    return true;
  }

  @override
  Future open(String? correlationId) async {
    // pass
  }

  @override
  Future close(String? correlationId) async {
    // pass
  }

  /// Clears component state.
  @override
  Future clear(String? correlationId) async {
    // pass
  }

  /// Unsets (clears) previously set references to dependent components.
  @override
  void unsetReferences() {
    // pass
  }
}


class MyComponentA
    implements
        IReferenceable,
        IUnreferenceable,
        IConfigurable,
        IOpenable,
        ICleanable {
  String _param1 = 'ABC';
  int _param2 = 123;

  bool _opened = false;

  String? _status;

  MyComponentB? _anotherComponent;

  String? dummyVariable = 'resource variable';

  var _logger = ConsoleLogger();

  /// Creates a new instance of the component.
  MyComponentA() : _status = 'Created' {
    _logger.setLevel(LogLevel.Debug);
    _logger.info(null, 'MyComponentA has been configured.');
  }

  @override
  void configure(ConfigParams config) {
    _param1 = config.getAsStringWithDefault('param1', _param1);
    _param2 = config.getAsIntegerWithDefault('param2', _param2);
    _status = 'Configured';

    _logger.info(null, 'MyComponentB has been configured.');
  }

  void myTask(String? correlationId) {
    // create an artificial problem
    try {
      throw Exception('Logging demo. Error created');
    } catch (ex) {
      _logger.error(correlationId, ex as Exception, 'Error created.');
    }
  }

  @override
  void setReferences(IReferences references) {
    _anotherComponent = references.getOneRequired(
        Descriptor('myservice', 'mycomponent-b', '*', '*', '1.0'));
    _status = 'Configured';
    _logger.info(null, "MyComponentA's references have been defined.");
  }

  @override
  bool isOpen() {
    return _opened;
  }

  @override
  Future open(String? correlationId) async {
    _opened = true;
    _status = 'Open';
    _logger.info(correlationId, 'MyComponentA has been opened.');
    // artificial problem
    myTask(correlationId);
  }

  @override
  Future close(String? correlationId) async {
    _opened = false;
    _status = 'Closed';
    _logger.info(correlationId, 'MyComponentA has been closed.');
  }

  /// Clears component state.
  @override
  Future clear(String? correlationId) async {
    dummyVariable = null;
    _status = null;
    _logger.info(null, 'Resources cleared');
  }

  /// Unsets (clears) previously set references to dependent components.
  @override
  void unsetReferences() {
    _anotherComponent = null;
    _status = 'Un-referenced';
    _logger.info(null, 'References cleared');
  }
}


from pip_services3_commons.config import IConfigurable, ConfigParams, IExecutable
from pip_services3_commons.refer import IReferenceable, IReferences, Descriptor, IUnreferenceable
from pip_services3_commons.run import IOpenable, ICleanable
from pip_services3_components.log import ConsoleLogger, LogLevel

class MyComponentB(IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable):
    _param1 = 'ABC2'
    _param2 = 456
    _opened = False

    dummy_variable = "resource variable"

    def __init__(self):
        """
        Creates a new instance of the component.
        """
        self._status = "Created"
        logger.info(None, "MyComponentB has been created.")
        
    def configure(self, config):
        self._param1 = config.get_as_string_with_default("param1", self._param1)
        self._param2 = config.get_as_integer_with_default("param2", self._param2)
        logger.info(None, "MyComponentB has been configured.")
        
    def set_references(self, references):
        pass
        
    def is_open(self):
        pass

    def open(self, correlation_id):
        pass

    def close(self, correlation_id):
        pass
        
    def my_task(self, correlation_id):
        pass

    def unset_references(self):
        """
        Unsets (clears) previously set references to dependent components.
        """
        pass
    
    def clear(self, correlation_id):
        """
        Clears component state.
        :param correlation_id: (optional) transaction id to trace execution through call chain.
        """
        pass



class MyComponentA(IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable, IExecutable):
    _param1 = 'ABC'
    _param2 = 123
    _another_component: MyComponentB
    _open = False
    _status = None
    
    def __init__(self):
        """
        Creates a new instance of the component.
        """
        self._status = "Created"
        
        logger.info(None, "MyComponentA has been created.")

            
    def configure(self, config):
        self._param1 = config.get_as_string_with_default("param1", 'ABC')
        self._param2 = config.get_as_integer_with_default("param2", 123)
        self._status = "Configured"
        logger.info(None,"MyComponentA has been configured.")

    def set_references(self, references):
        self._another_component = references.get_one_required(
            Descriptor("myservice", "mycomponent-b", "*", "*", "1.0")
        )
        self._status = "Configured"
        logger.info(None,"MyComponentA's references have been defined.")
        
    def is_open(self):
        return self._open

    def open(self, correlation_id):
        self._open = True
        self._status = "Open"
        logger.info(None,"MyComponentA has been opened.")
        # artificial problem
        self.my_task(correlation_id)

    def close(self, correlation_id):
        self._opened = False
        self._status = "Closed"
        logger.info(correlation_id,"MyComponentA has been closed.")
        
    def my_task(self, correlation_id):
        # create an artificial problem        
        try:
            raise Exception('Logging demo', 'Error created')
        except Exception as inst:
            logger.error(correlation_id, inst, "Error created.")  

    def unset_references(self):
        """
        Unsets (clears) previously set references to dependent components.
        """
        self._another_component = None
        self._status = "Un-referenced"
        logger.info(None, "References cleared")
    
    def clear(self, correlation_id):
        """
        Clears component state.
        :param correlation_id: (optional) transaction id to trace execution through call chain.
        """
        self.dummy_variable = None
        self._status = None
        logger.info(correlation_id, "Resources cleared")
                
Not available

b) Our factory


// Creating a factory

import { Factory } from 'pip-services3-components-nodex';

let MyFactory1 = new Factory();

MyFactory1.registerAsType(new Descriptor("myservice", "mycomponentA", "default", "*", "1.0"), MyComponentA)
MyFactory1.registerAsType(new Descriptor("myservice", "mycomponent-b", "default", "*", "1.0"), MyComponentB)

using PipServices3.Commons.Refer;
using PipServices3.Components.Build;


var MyFactory1 = new Factory();

MyFactory1.RegisterAsType(new Descriptor("myservice", "mycomponentA", "default", "*", "1.0"), typeof(MyComponentA));
MyFactory1.RegisterAsType(new Descriptor("myservice", "mycomponent-b", "default", "*", "1.0"), typeof(MyComponentB));

// Creating a factory
import (
	cbuild "github.com/pip-services3-go/pip-services3-components-go/build"
)

MyFactory1 := cbuild.NewFactory()

MyFactory1.RegisterType(crefer.NewDescriptor("myservice", "mycomponentA", "default", "*", "1.0"), NewMyComponentA)
MyFactory1.RegisterType(crefer.NewDescriptor("myservice", "mycomponent-b", "default", "*", "1.0"), NewMyComponentB)

// Creating a factory
var MyFactory1 = Factory();

MyFactory1.registerAsType(
    Descriptor('myservice', 'mycomponentA', 'default', '*', '1.0'),
    MyComponentA);

MyFactory1.registerAsType(
    Descriptor('myservice', 'mycomponent-b', 'default', '*', '1.0'),
    MyComponentB);


# Creating a factory

from pip_services3_components.build import Factory 
MyFactory1 = Factory()

MyFactory1.register_as_type(Descriptor("myservice", "mycomponentA", "default", "*", "1.0"), MyComponentA)
MyFactory1.register_as_type(Descriptor("myservice", "mycomponent-b", "default", "*", "1.0"), MyComponentB)

Not available

c) Our service

// Creating a process container

class MyProcess extends ProcessContainer{
    public constructor() {
        super('myservice', 'My service running as a process');
        this._configPath = './config.yaml'
        this._factories.add(MyFactory1)
    }
}

using PipServices3.Components.Build;
using PipServices3.Components.Refer;
using PipServices3.Container;

    // Creating a process container

class MyProcess : ProcessContainer
{
    public MyProcess() : base("myservice", "My service running as a process")
    {
        this._configPath = "../../../../../config/config.yml";

        var MyFactory1 = new Factory();

        MyFactory1.RegisterAsType(new Descriptor("myservice", "mycomponentA", "default", "*", "1.0"), typeof(MyComponentA));
        MyFactory1.RegisterAsType(new Descriptor("myservice", "mycomponent-b", "default", "*", "1.0"), typeof(MyComponentB));

        this._factories.Add(MyFactory1);
    }
}

// Creating a process container
type MyProcess struct {
	cproc.ProcessContainer
}

func NewMyProcess() *MyProcess {
	c := MyProcess{}
	c.ProcessContainer = *cproc.NewProcessContainer("myservice", "My service running as a process")
	c.SetConfigPath("./config/config.yml")
	MyFactory1 := cbuild.NewFactory()

	MyFactory1.RegisterType(crefer.NewDescriptor("myservice", "mycomponentA", "default", "*", "1.0"), NewMyComponentA)
	MyFactory1.RegisterType(crefer.NewDescriptor("myservice", "mycomponent-b", "default", "*", "1.0"), NewMyComponentB)

	c.AddFactory(MyFactory1)
	return &c
}

// Creating a process container

class MyProcess extends ProcessContainer {
  MyProcess() : super('myservice', 'My service running as a process') {
    configPath = './config.yaml';
    factories.add(MyFactory1);
  }
}

# Creating a process container

from pip_services3_container import ProcessContainer

class MyProcess(ProcessContainer):
    def __init__(self):
        super(MyProcess, self).__init__('myservice', 'My service running as a process')
        self._config_path = 'E:\\config2.yaml'
        self._factories.add(MyFactory1)
  
Not available

d) The dynamic configuration file for the components:

config.yaml

---
- descriptor: "myservice:mycomponentA:*:default:1.0"
  param1: XYZ
  param2: 987

- descriptor: myservice:mycomponent-b:*:*:1.0
  param1: XYZ
  param2: 987

e) Running our service

let MyProcess = require('./obj/example').MyProcess;

try {
    let proc = new MyProcess();
    proc.run(process.argv);
} catch (ex) {
    console.error(ex);
}

using System;

namespace ExampleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var task = (new MyProcess()).RunAsync(args);
                task.Wait();
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex);
            }
        }
    }
}


package main

func main() {
	proc := NewMyProcess()
	proc.Run(os.Args)
}

void main(List<String> arguments) async {
  try {
    var proc = MyProcess();
    proc.run(arguments);
  } catch (ex) {
    print(ex);
  }
}

# -*- coding: utf-8 -*-

if __name__ == '__main__':
    runner = MyProcess()
    print("Run")
    try:
        runner.run()
    except Exception as ex:
        print(ex)

Not available

After running this code, we will get the following result:

Console logger messages

As we can see from the above figure, the program has logged all messages with level info and from our artificial error.

This concludes our first task.

Adding a composite logger to our component

Now, we will extend our logging capacity by adding a composite logger. This logger will allow us to aggregate all loggers from our component’s references into a centralized log. Our code will remain the same, except that now we need to create a composite logger for MyComponentA. For this, we will create an instance of this logger and set the logging level to 5.

Then, we will use the configure and set_references methods to let our composite logger know where to ask for log messages. Our factory and process container code sections will remain the same, but we will have to add a reference to our console logger in our configuration file. The syntax will be:

# Console logger
- descriptor: "pip-services:logger:console:default:1.0"
  level: {{LOG_LEVEL}}{{^LOG_LEVEL}}info{{/LOG_LEVEL}}

Finally, we will add a console logger to MyComponentB. After these changes, our component section will look like this:

import { ConfigParams, Descriptor, ICleanable, IOpenable, IUnreferenceable } from 'pip-services3-commons-nodex';
import { IConfigurable } from 'pip-services3-commons-nodex';
import { IReferences } from 'pip-services3-commons-nodex';
import { IReferenceable } from 'pip-services3-commons-nodex';
import { CompositeLogger, ConsoleLogger, Factory } from 'pip-services3-components-nodex';
import { ProcessContainer } from 'pip-services3-container-nodex';

export class MyComponentB implements IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable {
    private _param1: string = 'ABC2';
    private _param2: number = 456;
    private _opened:boolean = false;

    private _status: string;

    private _logger = new ConsoleLogger();
     

    /**
     * Creates a new instance of the component.
     */
    public constructor(){
        this._status = 'Created';
        this._logger.setLevel(5);
        this._logger.info(null, "MyComponentB has been configured.");

    }
    
    public configure(config: ConfigParams): void {
        this._param1 = config.getAsStringWithDefault('param1', this._param1);
        this._param2 = config.getAsIntegerWithDefault('param2', this._param2);

        this._logger.info(null, "MyComponentB has been configured.")
    }

    public setReferences(refs: IReferences): void {
        // pass
    }

    public isOpen(): boolean {
        // pass
    }
    public open(correlationId: string): Promise<void> {
        // pass
    }
    public close(correlationId: string): Promise<void> {
        // pass
    }

    public myTask(correlationId: string): void {
        // create an artificial problem
        try {
            throw Error('Logging demo. Error created');
        }
        catch (ex) {
            this._logger.error(correlationId, ex, "Error created.")
        }

    }

    /**
     * Unsets (clears) previously set references to dependent components.
     */
    public unsetReferences(): void {
        // pass
    }

    /**
     * Clears component state.
     * @param correlationId (optional) transaction id to trace execution through call chain.
     */
    public clear(correlationId: string): Promise<void> {
        // pass
    }
}

export class MyComponentA implements IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable {
    private _param1: string = 'ABC';
    private _param2: number = 123;
    private _opened: boolean = false;

    private _status: string;

    private _another_component: MyComponentB;

    public dummy_variable = "resource variable";

    private _logger = new CompositeLogger();


    /**
     * Creates a new instance of the component.
     */
    public constructor() {
        this._status = 'Created';
        this._logger.setLevel(5);
        this._logger.info(null, "MyComponentA has been configured.");

    }

    public configure(config: ConfigParams): void {
        this._param1 = config.getAsStringWithDefault('param1', this._param1);
        this._param2 = config.getAsIntegerWithDefault('param2', this._param2);
        this._status = "Configured";

        this._logger.info(null, "MyComponentA has been configured.")
    }

    public setReferences(refs: IReferences): void {
        this._another_component = refs.getOneRequired(
            new Descriptor("myservice", "mycomponent-b", "*", "*", "1.0")
        )
        this._status = "Configured"
        this._logger.setReferences(refs);

        this._logger.info(null, "MyComponentA's references have been defined.")
    }

    public isOpen(): boolean {
        return this._opened;
    }
    public open(correlationId: string): Promise<void> {
        this._opened = true;
        this._status = "Open";
        this._logger.info(correlationId, "MyComponentA has been opened.")
        // artificial problem
        this.myTask(correlationId);
    }
    public close(correlationId: string): Promise<void> {
        this._opened = false;
        this._status = "Closed";
        this._logger.info(correlationId, "MyComponentA has been closed.");
    }

    public myTask(correlationId: string): void {
        // create an artificial problem
        try {
            throw Error('Logging demo. Error created');
        }
        catch (ex) {
            this._logger.error(correlationId, ex, "Error created.")
        }

    }

    /**
     * Unsets (clears) previously set references to dependent components.
     */
    public unsetReferences(): void {
        this._another_component = null;
        this._status = "Un-referenced";
        this._logger.info(null, "References cleared");
    }

    /**
     * Clears component state.
     * @param correlationId (optional) transaction id to trace execution through call chain.
     */
    public clear(correlationId: string): Promise<void> {
        this.dummy_variable = null;
        this._status = null;
        this._logger.info(null, "Resources cleared");
    }
}


using PipServices3.Commons.Config;
using PipServices3.Commons.Refer;
using PipServices3.Commons.Run;
using PipServices3.Components.Log;
using PipServices3.Container;

using System;

using System.Threading.Tasks;

namespace ExampleApp
{
    class MyComponentB : IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable
    {
        private string _param1 = "ABC2";
        private int _param2 = 456;
        private bool _opened = false;

        private string _status;

        private ConsoleLogger _logger = new ConsoleLogger();

        /// <summary>
        /// Creates a new instance of the component.
        /// </summary>
        public MyComponentB()
        {
            this._status = "Created";
            this._logger.Level = LogLevel.Debug;
            this._logger.Info(null, "MyComponentB has been configured.");
        }

        public void Configure(ConfigParams config)
        {
            this._param1 = config.GetAsStringWithDefault("param1", this._param1);
            this._param2 = config.GetAsIntegerWithDefault("param2", this._param2);

            this._logger.Info(null, "MyComponentB has been configured.");
        }

        public void SetReferences(IReferences references)
        {

        }

        public bool IsOpen()
        {
            return _opened;
        }

        public async Task OpenAsync(string correlationId)
        {
            await Task.FromResult(0);
        }


        public async Task CloseAsync(string correlationId)
        {
            await Task.FromResult(0);
        }

        public void MyTask(string correlationId)
        {
            var logger = new ConsoleLogger();
            logger.Level = LogLevel.Debug;
            // create an artificial problem
            try
            {
                throw new Exception("Logging demo. Error created");
            }
            catch (Exception e)
            {
                logger.Error(correlationId, e, "Error created.");
            }
        }

        /// <summary>
        /// Unsets (clears) previously set references to dependent components.
        /// </summary>
        public void UnsetReferences()
        {

        }

        /// <summary>
        /// Clears component state.
        /// </summary>
        /// <param name="correlationId">(optional) transaction id to trace execution through call chain.</param>
        /// <returns></returns>
        public async Task ClearAsync(string correlationId)
        {
            await Task.FromResult(0);
        }

    }


    class MyComponentA : IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable
    {
        private string _param1 = "ABC";
        private int _param2 = 123;
        private bool _opened = false;
        private string _status;

        private MyComponentB _another_component;

        private CompositeLogger _logger = new CompositeLogger();

        public string dummy_variable = "resource variable";

        /// <summary>
        /// Creates a new instance of the component.
        /// </summary>
        public MyComponentA()
        {
            this._status = "Created";
            this._logger.Level = LogLevel.Debug;
            this._logger.Info(null, "MyComponentA has been configured.");
        }

        public void Configure(ConfigParams config)
        {
            this._param1 = config.GetAsStringWithDefault("param1", this._param1);
            this._param2 = config.GetAsIntegerWithDefault("param2", this._param2);
            this._status = "Configured";

            this._logger.Info(null, "MyComponentA has been configured.");
        }

        public void SetReferences(IReferences references)
        {
            this._another_component = references.GetOneRequired(
                new Descriptor("myservice", "mycomponent-b", "*", "*", "1.0")
            ) as MyComponentB;
            this._logger.SetReferences(references);

            this._status = "Configured";
            this._logger.Info(null, "MyComponentA's references have been defined.");
        }

        public bool IsOpen()
        {
            return this._opened;
        }

        public async Task OpenAsync(string correlationId)
        {
            this._opened = true;
            this._status = "Open";
            this._logger.Info(correlationId, "MyComponentA has been opened.");
            // artificial problem
            this.MyTask(correlationId);

            await Task.FromResult(0);
        }


        public async Task CloseAsync(string correlationId)
        {
            this._opened = false;
            this._status = "Closed";
            this._logger.Info(correlationId, "MyComponentA has been closed.");

            await Task.FromResult(0);
        }

        public void MyTask(string correlationId)
        {
            var logger = new ConsoleLogger();
            logger.Level = LogLevel.Debug;
            // create an artificial problem
            try
            {
                throw new Exception("Logging demo. Error created");
            }
            catch (Exception e)
            {
                logger.Error(correlationId, e, "Error created.");
            }
        }

        /// <summary>
        /// Unsets (clears) previously set references to dependent components.
        /// </summary>
        public void UnsetReferences()
        {
            this._another_component = null;
            this._status = "Un-referenced";
            this._logger.Info(null, "References cleared");
        }

        /// <summary>
        /// Clears component state.
        /// </summary>
        /// <param name="correlationId">(optional) transaction id to trace execution through call chain.</param>
        /// <returns></returns>
        public async Task ClearAsync(string correlationId)
        {
            this.dummy_variable = null;
            this._status = null;
            this._logger.Info(null, "Resources cleared");

            await Task.FromResult(0);
        }

    }
}



import (
	"errors"

	cconfig "github.com/pip-services3-go/pip-services3-commons-go/config"
	crefer "github.com/pip-services3-go/pip-services3-commons-go/refer"
	crun "github.com/pip-services3-go/pip-services3-commons-go/run"
	cbuild "github.com/pip-services3-go/pip-services3-components-go/build"
	clog "github.com/pip-services3-go/pip-services3-components-go/log"
	cproc "github.com/pip-services3-go/pip-services3-container-go/container"
)

type MyComponentB struct {
	crefer.IReferences
	crefer.IUnreferenceable
	cconfig.IConfigurable
	crun.IOpenable
	crun.ICleanable

	_logger *clog.ConsoleLogger

	_status string
	_param1 string
	_param2 int
	_opened bool
}

// Creates a new instance of the component.
func NewMyComponentB() *MyComponentB {
	component := &MyComponentB{
		_status: "Created",
		_param1: "ABC2",
		_param2: 456,
		_opened: false,
		_logger: clog.NewConsoleLogger(),
	}

	component._logger.SetLevel(5)
	component._logger.Info("123", "MyComponentB has been created.")

	return component
}

func (c *MyComponentB) Configure(config *cconfig.ConfigParams) {
	c._param1 = config.GetAsStringWithDefault("param1", c._param1)
	c._param2 = config.GetAsIntegerWithDefault("param2", c._param2)

	c._logger.Info("123", "MyComponentB has been configured.")
}

func (c *MyComponentB) SetReferences(references *crefer.References) {
	// pass
}

func (c *MyComponentB) isOpen() bool {
	// pass
	return true
}

func (c *MyComponentB) Open(correlationId string) {
	// pass
}

func (c *MyComponentB) Close(correlationId string) {
	// pass
}

func (c *MyComponentB) MyTask(correlationId string) {
	// pass
}

// Unsets (clears) previously set references to dependent components.
func (c *MyComponentB) UnsetReferences() {
	// pass
}

// Clears component state.
// - correlationId: (optional) transaction id to trace execution through call chain.
func (c *MyComponentB) Clear(correlationId string) {
	// pass
}

type MyComponentA struct {
	crefer.IReferences
	crefer.IUnreferenceable
	cconfig.IConfigurable
	crun.IOpenable
	crun.ICleanable

	_logger *clog.CompositeLogger

	_status string
	_param1 string
	_param2 int
	_opened bool

	dummy_variable interface{}

	_another_component interface{}
}

// Creates a new instance of the component.
func NewMyComponentA() *MyComponentA {
	component := &MyComponentA{
		_status: "Created",
		_param1: "ABC2",
		_param2: 456,
		_opened: false,
		_logger: clog.NewCompositeLogger(),

		dummy_variable: "dummy_variable setted",
	}

	component._logger.SetLevel(5)
	component._logger.Info("123", "MyComponentA has been created.")

	return component
}

func (c *MyComponentA) Configure(config *cconfig.ConfigParams) {
	c._param1 = config.GetAsStringWithDefault("param1", c._param1)
	c._param2 = config.GetAsIntegerWithDefault("param2", c._param2)
	c._status = "Configured"

	c._logger.Info("123", "MyComponentB has been configured.")
}

func (c *MyComponentA) SetReferences(references *crefer.References) {
	_another_component, err := references.GetOneRequired(crefer.NewDescriptor("myservice", "mycomponent-b", "*", "*", "1.0"))

	if err != nil {
		panic("Error: Another Component is not in refs")
	}

	c._another_component = _another_component.(MyComponentB)
	c._status = "Configured"

	c._logger.Info("123", "MyComponentA's references have been defined.")
}

func (c *MyComponentA) isOpen() bool {
	return c._opened
}

func (c *MyComponentA) Open(correlationId string) {
	c._opened = true
	c._status = "Open"
	c._logger.Info("123", "MyComponentA has been opened.")
	// artificial problem
	c.MyTask(correlationId)
}

func (c *MyComponentA) Close(correlationId string) {
	c._opened = false
	c._status = "Closed"
	c._logger.Info(correlationId, "MyComponentA has been closed.")
}

func (c *MyComponentA) MyTask(correlationId string) {
	// create an artificial problem
	c._logger.Error(correlationId, errors.New("Logging demo. Error created"), "Error created.")
}

// Unsets (clears) previously set references to dependent components.
func (c *MyComponentA) UnsetReferences() {
	c._another_component = nil
	c._status = "Un-referenced"
	c._logger.Info("123", "References cleared")
}

// Clears component state.
// - correlationId: (optional) transaction id to trace execution through call chain.
func (c *MyComponentA) Clear(correlationId string) {
	c.dummy_variable = nil
	c._status = ""
	c._logger.Info(correlationId, "Resources cleared")
}

// Creating a process container
type MyProcess struct {
	cproc.ProcessContainer
}

func NewMyProcess() *MyProcess {
	c := MyProcess{}
	c.ProcessContainer = *cproc.NewProcessContainer("myservice", "My service running as a process")
	c.SetConfigPath("./config.yml")

	MyFactory1 := cbuild.NewFactory()

	MyFactory1.RegisterType(crefer.NewDescriptor("myservice", "mycomponentA", "default", "*", "1.0"), NewMyComponentA)
	MyFactory1.RegisterType(crefer.NewDescriptor("myservice", "mycomponent-b", "default", "*", "1.0"), NewMyComponentB)

	c.AddFactory(MyFactory1)
	return &c
}


class MyComponentB
    implements
        IReferenceable,
        IUnreferenceable,
        IConfigurable,
        IOpenable,
        ICleanable {
  String _param1 = 'ABC2';
  int _param2 = 456;

  bool _opened = false;

  String? _status;

  var _logger = ConsoleLogger();

  /// Creates a new instance of the component.
  MyComponentB() {
    _status = 'Created';
    _logger.setLevel(LogLevel.Debug);
    _logger.info(null, 'MyComponentB has been configured.');
  }

  @override
  void configure(ConfigParams config) {
    _param1 = config.getAsStringWithDefault('param1', _param1);
    _param2 = config.getAsIntegerWithDefault('param2', _param2);
    _logger.info(null, 'MyComponentB has been configured.');
  }

  void myTask(String? correlationId) {
    // create an artificial problem
    try {
      throw Exception('Logging demo. Error created');
    } catch (ex) {
      _logger.error(correlationId, ex as Exception, 'Error created.');
    }
  }

  @override
  void setReferences(IReferences references) {
    // pass
  }

  @override
  bool isOpen() {
    // pass
    return true;
  }

  @override
  Future open(String? correlationId) async {
    // pass
  }

  @override
  Future close(String? correlationId) async {
    // pass
  }

  /// Clears component state.
  @override
  Future clear(String? correlationId) async {
    // pass
  }

  /// Unsets (clears) previously set references to dependent components.
  @override
  void unsetReferences() {
    // pass
  }
}

class MyComponentA
    implements
        IReferenceable,
        IUnreferenceable,
        IConfigurable,
        IOpenable,
        ICleanable {
  String _param1 = 'ABC';
  int _param2 = 123;

  bool _opened = false;

  String? _status;

  MyComponentB? _anotherComponent;

  String? dummyVariable = 'resource variable';

  var _logger = CompositeLogger();

  /// Creates a new instance of the component.
  MyComponentA() : _status = 'Created' {
    _logger.setLevel(LogLevel.Debug);
    _logger.info(null, 'MyComponentA has been configured.');
  }

  @override
  void configure(ConfigParams config) {
    _param1 = config.getAsStringWithDefault('param1', _param1);
    _param2 = config.getAsIntegerWithDefault('param2', _param2);

    _status = 'Configured';

    _logger.info(null, 'MyComponentB has been configured.');
  }

  void myTask(String? correlationId) {
    // create an artificial problem
    try {
      throw Exception('Logging demo. Error created');
    } catch (ex) {
      _logger.error(correlationId, ex as Exception, 'Error created.');
    }
  }

  @override
  void setReferences(IReferences references) {
    _anotherComponent = references.getOneRequired(
        Descriptor('myservice', 'mycomponent-b', '*', '*', '1.0'));
    _logger.setReferences(references);

    _status = 'Configured';
    _logger.info(null, "MyComponentA's references have been defined.");
  }

  @override
  bool isOpen() {
    return _opened;
  }

  @override
  Future open(String? correlationId) async {
    _opened = true;
    _status = 'Open';
    _logger.info(correlationId, 'MyComponentA has been opened.');
    // artificial problem
    myTask(correlationId);
  }

  @override
  Future close(String? correlationId) async {
    _opened = false;
    _status = 'Closed';
    _logger.info(correlationId, 'MyComponentA has been closed.');
  }

  /// Clears component state.
  @override
  Future clear(String? correlationId) async {
    dummyVariable = null;
    _status = null;
    _logger.info(null, 'Resources cleared');
  }

  /// Unsets (clears) previously set references to dependent components.
  @override
  void unsetReferences() {
    _anotherComponent = null;
    _status = 'Un-referenced';
    _logger.info(null, 'References cleared');
  }
}


from pip_services3_commons.config import IConfigurable, ConfigParams
from pip_services3_commons.refer import IReferenceable, IReferences, Descriptor, IUnreferenceable
from pip_services3_commons.run import IOpenable, ICleanable
from pip_services3_components.log import ConsoleLogger, LogLevel, CompositeLogger

class MyComponentB(IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable):
    _param1 = 'ABC2'
    _param2 = 456
    _opened = False

    __logger = ConsoleLogger()
    __logger.set_level(5)

    dummy_variable = "resource variable"
    
    def __init__(self):
        """
        Creates a new instance of the component.
        """
        self._status = "Created"
        self.__logger.info(None, "MyComponentB has been created.")
        
    def configure(self, config):
        self._param1 = config.get_as_string_with_default("param1", self._param1)
        self._param2 = config.get_as_integer_with_default("param2", self._param2)
        
    def set_references(self, references):
        pass
        
    def is_open(self):
        pass

    def open(self, correlation_id):
        pass

    def close(self, correlation_id):
        pass
        
    def my_task(self, correlation_id):
        pass

    def unset_references(self):
        """
        Unsets (clears) previously set references to dependent components.
        """
        pass
    
    def clear(self, correlation_id):
        """
        Clears component state.
        :param correlation_id: (optional) transaction id to trace execution through call chain.
        """
        pass



class MyComponentA(IReferenceable, IUnreferenceable, IConfigurable, IOpenable, ICleanable):
    _param1 = 'ABC'
    _param2 = 123
    _another_component: MyComponentB
    _open = False
    _status = None
    __logger = CompositeLogger()
    __logger.set_level(5)

    def __init__(self):
        """
        Creates a new instance of the component.
        """
        self._status = "Created"
        
        self.__logger.info(None, "MyComponentA has been created.")

            
    def configure(self, config):
        self._param1 = config.get_as_string_with_default("param1", 'ABC')
        self._param2 = config.get_as_integer_with_default("param2", 123)
        self._status = "Configured"
        self.__logger.configure(config)
        

    def set_references(self, references):
        self._another_component = references.get_one_required(
            Descriptor("myservice", "mycomponent-b", "*", "*", "1.0")
        )
        self._status = "Configured"
        self.__logger.set_references(references)
        self.__logger.info(None,"MyComponentA's references have been defined.")
        
    def is_open(self):
        return self._open

    def open(self, correlation_id):
        self._open = True
        self._status = "Open"
        self.__logger.info(None,"MyComponentA has been opened.")
        # artificial problem
        self.my_task(correlation_id)

    def close(self, correlation_id):
        self._opened = False
        self._status = "Closed"
        self.__logger.info(correlation_id,"MyComponentA has been closed.")
        
    def my_task(self, correlation_id):
        # create an artificial problem        
        try:
            raise Exception('Logging demo', 'Error created')
        except Exception as inst:
            self.__logger.error(correlation_id, inst, "Error created.")  

    def unset_references(self):
        """
        Unsets (clears) previously set references to dependent components.
        """
        self._another_component = None
        self._status = "Un-referenced"
        self.__logger.info(None, "References cleared")
    
    def clear(self, correlation_id):
        """
        Clears component state.
        :param correlation_id: (optional) transaction id to trace execution through call chain.
        """
        self.dummy_variable = None
        self._status = None
        self.__logger.info(correlation_id, "Resources cleared")
        
Not available

Once we run our service with the re-defined components, we will get the following results:

Composite logger messages

As we can see, we have log messages received from both MyComponentA and MyComponentB.

Adding specific loggers

As we said earlier, PIP.Services has specific loggers for Datadog, Elasticsearch, and Amazon CloudWatch. The process to add any of them to a component is similar to what we saw in our console logger example: we need to declare an instance of the logger, configure it, set the message level, and add the messages we need. Here below are examples of how to define each of them.

a) Datadog

import { DataDogLogger } from 'pip-services-datadog-nodex';

let logger = new DataDogLogger();

logger.configure(ConfigParams.fromTuples(
    "credential.access_key", "827349874395872349875493"
));

logger.setLevel(5);

logger.info("123", "My message");

using PipServices3.Commons.Config;
using PipServices3.Components.Log;
using PipServices3.DataDog.Log;

namespace ExampleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var logger = new DataDogLogger();

            logger.Configure(ConfigParams.FromTuples(
                "credential.access_key", "827349874395872349875493"
            ));

            logger.Level = LogLevel.Debug;

            logger.Info("123", "My message");
        }
    }
}


package main

import (
	cconfig "github.com/pip-services3-go/pip-services3-commons-go/config"
	logdatadog "github.com/pip-services3-go/pip-services3-datadog-go/log"
)

func main() {

	logger := logdatadog.NewDataDogLogger()

	logger.Configure(cconfig.NewConfigParamsFromTuples(
		"credential.access_key", "827349874395872349875493",
	))

	logger.SetLevel(5)

	logger.Info("123", "My message")

}


import 'package:pip_services3_datadog/pip_services3_datadog.dart';

var logger = DataDogLogger();

logger.configure(ConfigParams.fromTuples([
    'credential.access_key', '827349874395872349875493']));

logger.setLevel(LogLevel.Debug);

logger.info('123', 'My message');


from pip_services3_datadog.log.DataDogLogger import DataDogLogger
from pip_services3_commons.config import ConfigParams

logger = DataDogLogger()

logger.configure(ConfigParams.from_tuples(
   "credential.access_key", "827349874395872349875493"
))

logger.set_level(5)

logger.info("123" , "My message")

Not available

b) Elasticsearch

import { ElasticSearchLogger } from 'pip-services-elasticsearch-nodex';

let logger = new ElasticSearchLogger();

logger.configure(ConfigParams.fromTuples(
    "connection.protocol", "http",
    "connection.host", "localhost",
    "connection.port", 9200
));

logger.setLevel(5);

logger.info("123", "My message");

using PipServices3.Commons.Config;
using PipServices3.Components.Log;
using PipServices3.ElasticSearch.Log;

namespace ExampleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var logger = new ElasticSearchLogger();

            logger.Configure(ConfigParams.FromTuples(
                "connection.protocol", "http",
                "connection.host", "localhost",
                "connection.port", 9200
            ));

            logger.Level = LogLevel.Debug;

            logger.Info("123", "My message");
        }
    }
}



package main

import (
	cconfig "github.com/pip-services3-go/pip-services3-commons-go/config"
	logelastic "github.com/pip-services3-go/pip-services3-elasticsearch-go/log"
)

func main() {

	logger := logelastic.NewElasticSearchLogger()
	logger.Configure(cconfig.NewConfigParamsFromTuples(
		"connection.protocol", "http",
		"connection.host", "localhost",
		"connection.port", 9200,
	))

	logger.SetLevel(5)

	logger.Info("123", "My message")

}


import 'package:pip_services3_elasticsearch/pip_services3_elasticsearch.dart';

var logger = ElasticSearchLogger();
logger.configure(ConfigParams.fromTuples([
  'connection.protocol',
  'http',
  'connection.host',
  'localhost',
  'connection.port',
  9200
]));
logger.setLevel(LogLevel.Debug);
logger.info('123', 'My message');


from pip_services3_elasticsearch.log.ElasticSearchLogger import ElasticSearchLogger
from pip_services3_commons.config import ConfigParams

logger = ElasticSearchLogger()
logger.configure(ConfigParams.from_tuples(
    "connection.protocol", "http",
    "connection.host", "localhost",
    "connection.port", 9200
))

logger.set_level(5)

logger.info("123" , "My message")

Not available

c) Amazon CloudWatch

import { CloudWatchLogger } from 'pip-services3-aws-nodex';

let logger = new CloudWatchLogger();

logger.configure(ConfigParams.fromTuples(
    "stream", "mystream",
    "group", "mygroup",
    "connection.region", "us-east-1",
    "connection.access_id", "XXXXXXXXXXX",
    "connection.access_key", "XXXXXXXXXXX"
));

logger.setLevel(5);

logger.info("123", "My message");

using PipServices3.Aws.Log;
using PipServices3.Commons.Config;
using PipServices3.Components.Log;

namespace ExampleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var logger = new CloudWatchLogger();

            logger.Configure(ConfigParams.FromTuples(
                "stream", "mystream",
                "group", "mygroup",
                "connection.region", "us-east-1",
                "connection.access_id", "XXXXXXXXXXX",
                "connection.access_key", "XXXXXXXXXXX"
            ));

            logger.Level = LogLevel.Debug;

            logger.Info("123", "My message");
        }
    }
}


package main

import (
	cconfig "github.com/pip-services3-go/pip-services3-commons-go/config"
	logaws "github.com/pip-services3-go/pip-services3-aws-go/log"
)

func main() {

	logger := logaws.NewCloudWatchLogger()
	logger.Configure(cconfig.NewConfigParamsFromTuples(
		"stream", "mystream",
		"group", "mygroup",
		"connection.region", "us-east-1",
		"connection.access_id", "XXXXXXXXXXX",
		"connection.access_key", "XXXXXXXXXXX",
	))

	logger.SetLevel(5)

	logger.Info("123", "My message")
}

import 'package:pip_services3_aws/pip_services3_aws.dart';

var logger = CloudWatchLogger();

logger.configure(ConfigParams.fromTuples([
  'stream',
  'mystream',
  'group',
  'mygroup',
  'connection.region',
  'us-east-1',
  'connection.access_id',
  'XXXXXXXXXXX',
  'connection.access_key',
  'XXXXXXXXXXX'
]));

logger.setLevel(LogLevel.Debug);
logger.info('123', 'My message');


from pip_services3_aws.log.CloudWatchLogger import CloudWatchLogger
from pip_services3_commons.config import ConfigParams

logger = CloudWatchLogger()
logger.configure(ConfigParams.from_tuples(
    "stream", "mystream",
    "group", "mygroup",
    "connection.region", "us-east-1",
    "connection.access_id", "XXXXXXXXXXX",
    "connection.access_key", "XXXXXXXXXXX"
))

logger.set_level(5)

logger.info("123" , "My message")

Not available

Wrapping up

In this tutorial, we have learned what logging is, the different logging levels, and how to use the ConsoleLogger and CompositeLogger from PIP.Services to display log messages. The main advantage of the composite logger is its capacity to aggregate all logging messages, thus creating a centralized logging point.

We have also learned that PIP.Services provides several implementations of loggers, such as CloudWatchLogger, ElasticSearchLogger, and DataDogLogger.

Although the examples presented here are quite general, the concepts learned continue to apply to the development of more complex applications.