Skip to content
This repository was archived by the owner on Dec 5, 2022. It is now read-only.

RFC for providing logger information from runsettings #111

Merged
merged 9 commits into from
Jan 12, 2018

Conversation

abhishkk
Copy link
Contributor

@abhishkk abhishkk commented Jan 9, 2018

RFC is about adding support for providing logger information from the runsettings.


```xml
<RunSettings>
<LoggerRunSettings>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of LoggerRunSettings node? Did we consider RunSettings > Loggers > Logger?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoggerRunSettings node is added to be consistent with the way data collectors are specified in run settings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Thanks for keeping these consistent.


## Summary
This note outlines the proposed changes for:
1. Enabling logger support in test platform for TranslationLayer clients.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We support both protocol based and C# library based clients, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will allow developers to specify trx or xunit or nunit loggers from Test Explorer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Correct. Changes will enable logger support for both protocol based and C# library based clients. I will correct the RFC.

  2. No, user can't specify loggers from test explorer. Loggers can be specified from command line and run settings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't VS allow specifying runsettings through Test | Test Settings menu? Do we block the flow anywhere if user specifies loggers in the runsettings there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loggers are supported in run settings from Test settings menu also.
Earlier, I thought the question was about some first class support for adding loggers directly from test explorer (and not run settings)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!


2. Allowing users to provide loggers from the runsettings enables:
* TranslationLayer clients to provide loggers to the test platform.
* Alternative way to provide loggers in addition to the existing command line logger argument.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Config as code. Users can commit a runsettings with required loggers and it doesn't need to be specified at each invocation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks. Will correct RFC

## Specifying a logger
Loggers can be provided to the test platform in one of the following ways:

1. "/Logger:"UriOrFriendlyName";Key1=Value1;Key2=Value2" This is a switch to vstest.console.exe that feeds logger information to the test platform. For instance:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this change anything w.r.t loggers specified in dotnet test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Command line behavior (vstest.console and dotnet test) is not changed.

1. If same logger is specified both in command line and run settings, command line takes precedence.
2. Multiple Loggers can be added in runsettings by adding additional Logger node in LoggerRunSettings.Loggers section.
3. Configuration is optional in Logger node.
4. Atleast one attribute among uri, friendlyName, assemblyQualifiedName should be present in Logger node.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: suggest using inline quotes for readability. E.g. Atleast one attribute among uri, friendlyName, assemblyQualifiedName should be present in Logger node.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. will make corrections.

<RunSettings>
<LoggerRunSettings>
<Loggers>
<Logger friendlyName="sampleLoggerwithParameters">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the errors user will see if a logger is incorrectly specified? Will the test run continue or it will be aborted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exceptions are thrown in following scenarios:

  1. LoggerRunSettings or Loggers node has attributes.
  2. LoggerRunSettings has any node other than Loggers.
  3. Loggers has any node other than Logger.
  4. Logger has any node other than Configuration.
  5. Logger node has any attribute other than uri, assemblyQualifiedName, friendlyName, or enabled.
  6. Invalid format uri is given.
  7. Unable to find logger using precedent attribute.
  8. If none of the attributes uri, assemblyName, friendlyName is given.

Warning are printed in following scenarios:

  1. Key value pair has empty or whitespace value. Example: <Key1></Key1>.

Duplicate keys in configuration is ignored and verbose is added for it.

I will add these details in RFC as well.
In addition, Logger node understands enabled attribute as well. I will update RFC regarding the same.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming exceptions will abort the test run.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exceptions will abort the test run.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I recall is that IDE also tries to read the runsettings. If something is wrong with settings, IDE aborts the run and request does not reach the TestPlatform.
Also make sure we catch these exceptions, give out an actionable message to user and dont crash vstest.console exe.


In reply to: 160580427 [](ancestors = 160580427)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I will ensure, that vstest.console does not crash and only test aborts.


2. TestLoggerManager will implement both ITestRunEventsHandler and ITestDiscoveryEventsHandler2. On receiving any run/discovery event, corresponding logger event will be triggered by TestLoggerManager.

3. ProxyOperationManager will manage TestLoggerManager as loggers can register to both execution and discovery events. ProxyOperationManager will do following to manage TestLoggerManager:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the alternatives considered?

Isn't ProxyOperationManager only responsible for initialization of the host and establishing the communication channel? Why is the proxy operation manager the right layer to add this logic?

Isn't managing extensions responsibility of Test Engine?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test run events are not propagated to test engine. ProxyExecutionManager receives these events. For data collectors also, we have thus created ProxyExecutionManagerWithDataCollection.

In proposed solution, ProxyExecutionManager will call TestLoggerManager events in addition to TestRunRequest events whenever event is received.

As TestLoggerManager needs to listen to both run events and discovery events, I am proposing it to be managed by ProxyOperationManager (as its the parent class).

Rather than considering ProxyOperationManager as only responsible for initialization of the host and communication channel, is it ok to consider this class as parent of ProxyExecutionManager and ProxyDiscoveryManager and thus add common functionality in it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for clarifications.

For data collectors also, we have thus created ProxyExecutionManagerWithDataCollection.

This is a decorator, in my opinion. It enriches existing functionality maintaining layering intact. E.g. the inner concerns ProxyExecutionManager or ProxyDiscoveryManager or ProxyOperationManager don't know if there's something called DataCollection. DC is a higher level concept.

As TestLoggerManager needs to listen to both run events and discovery events, I am proposing it to be managed by ProxyOperationManager (as its the parent class).
Rather than considering ProxyOperationManager as only responsible for initialization of the host and communication channel, is it ok to consider this class as parent of ProxyExecutionManager and ProxyDiscoveryManager and thus add common functionality in it?

It is not a good idea. We have to be careful with inheritance, adding functionality like so will break SRP. Also coupling of lower layers with external concerns will increase failure conditions. Please consider making it aspect based, or expose generic capabilities in the lower layers, and compose them. E.g. the lower layer can let the higher ones know when there are interesting events. Higher layer can do any modifications required, further forward them to consumers (extensions). Lower layer is not aware of loggers or any extensions, higher layer does. In case no loggers are specified, higher layer is no-op.

As I understand, there are already events on raw message (or deserialized message) received. Could we modify this, or may be listen to these and say raise events for the extensions to consume?

The pipelines for Discovery and Execution are separate today and merge only at the RequestSender. Suggest to keep it separate, so that they can evolve independently. Discovery/Execution related logger events could be part of their individual flows.

Copy link
Contributor Author

@abhishkk abhishkk Jan 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing the SRP break and suggestions. Can you help in clarifying few points?

Clarifications needed:

  1. Even tough ProxyExecutionManagerWithDataCollection maintains layering, I think it doesn't maintain is a relationship with ProxyExecutionManager. Data collectors doesn't extend/enrich the responsibility of ProxyExecutionManager and have totally different responsibility. The idea of adding logger extension in ProxyOperationManager was to maintain has a relationship. Please correct me if I am not understanding this properly.

  2. I like the approach of raising events from low layer to higher layer for logger extensions. Should we plan in future for moving data collectors also in similar fashion (Considering 1st point correct)?

Proposal as per suggestions:

Does following seems ok?

  1. Low layer (ProxyExecutionManager, ProxyDiscoveryManager) will hold list of event handlers and will raise events to all of these handlers whenever an event is received (Currently TestRunRequest's event handler is hold by this low layer).
  2. This list of event handlers will be passed to ProxyExecutionManager while calling its StartTestRun method. So API of IProxyExecutionManager will change from int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsHandler eventHandler); to int StartTestRun(TestRunCriteria testRunCriteria, List<ITestRunEventsHandler> eventHandlers);
  3. TestLoggerManager instance will be created in TestRunRequest constructor.
  4. Whenever TestRunRequest.ExecuteAsync is invoked, TestRunRequest will pass TestLoggerManager event handler (along with its own handler) to ProxyExecutionManager.StartTestRun
  5. Whenever TestRunRequest is disposed, TestLoggerManager's logger events will also be disposed.

This way low layer will have idea only about event handlers (and not extensions, loggers etc)

Copy link
Contributor

@codito codito Jan 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Even though ProxyExecutionManagerWithDataCollection...

It actually ends up implementing the same IProxyExecutionManager interface via ProxyExecutionManager inheritance. There's is a relationship. The enrichment happens in two ways: before starting the test run ensure datacollectors are started, and after end of the test run ensure datacollector attachments are collected. Apart from these two, a PEMWithDataCollection uses PEM for actual execution.

  1. ...Should we plan in future for moving data collectors also in similar fashion

It should be possible depending on granularity of events.

Proposal...

Let's chat a bit to discuss the pros and cons.

Another approach: there could be a new internal TestRunEvents entity which exposes various run related events. Interested parties like extensions/loggers etc. subscribe to it. We can provide an updated ITestRunEventsHandler implementation which broadcasts the events in TestRunEvents. Effectively we are getting rid of sending this as the event handlers everywhere within the engine.

ITestRunEventsHandler still remains an external concept used widely across translation layer, testhost, engine etc..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: As per discussion with Arun, we are managing TestLoggerManager from TestRunRequest now. TestPlatform will create the TestLoggerManager and initialize it.
TestRunRequest will hold the instance of TestLoggerManager and call its method whenever an event is recevied. On test run completionm testRunRequest will dispose the TestLoggerManager.

</RunSettings>
```

This loads and initializes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please specify the exact logger APIs and the parameters it will be invoked with (for the above runsettings sample)?

Copy link
Contributor Author

@abhishkk abhishkk Jan 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loggers from run settings mimics the behavior of loggers from command line (from logger initialization perspective).

Still, I will add logger API which will invoked while initialization in RFC. Thanks.

<Key2>Value2</Key2>
</Configuration>
</Logger>
<Logger uri="logger://sample/sampleLoggerWithoutParameters1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can user configure console logger in same way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. user can configure any logger including console in this way.

@@ -0,0 +1,85 @@
# 0016 Loggers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request to make the title and doc name little more elaborate. This may help understand the intent from looking at filename.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Will elaborate the title.

This loads and initializes:
* Logger with friendlyName="sampleLoggerwithParameters". Key1=Value1 and Key2=Value2 are passed as dictionary parameters to the logger while initialization.
* Logger with uri="logger://sample/sampleLoggerWithoutParameters1". FriendlyName is ignored in this case as uri takes more precedence.
* Logger with assemblyQualifiedName="Sample.Sample.Sample.SampleLogger, Sample.Sample.Logger, Version=0.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx". Uri and friendlyName are ignored in this case as assemblyQualifiedName takes more precedence.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And user can override any runsetting with command line params like so vstest.console foo.dll -- RunSettings.Loggers.Logger. Is this supported? And will the precedence order be respected in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is not supported.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the rationale for not supporting it? At the least we shouldn't block this please. It will complicate implementation as I understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the confusion. User can override logger runsetting from command line paramas. I will update this info in RFC. thanks

<Configuration>
<Key1>Value1</Key1>
<Key2>Value2</Key2>
</Configuration>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we consider passing the entire runsettings to each logger? This will allow a logger to have any custom parameter as required (similar to Datacollectors).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires API changes in logger extensibility. We are not taking this change as part of this RFC. But we are taking key values in configuration element from runsettings thinking these changes in mind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense.


## Design
Following are the proposed design changes for enabling logger support for TranslationLayer clients:
1. TestLoggerManager will manage all the loggers. TestLoggerManager will be responsible for:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it include console logger as well?

Copy link
Contributor Author

@abhishkk abhishkk Jan 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestRequestManager will add console logger node in runsettings with assemblyQualifiedName attribute pointing to console logger type.
TestLoggerManager reads runsettings and initializes logger based on assemblyQualifiedName, uri and friendlyName.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense.

2. TestPlatform will create and initialize TestLoggerManager and will pass this TestLoggerManager instance to TestRunRequest and DiscoveryRequest.
3. TestRunRequest and DiscoveryRequest will hold the TestLoggerManager instance. On receving any run/discovery events, respective methods of TestLoggerManager will be invoked. i.e. TestLoggerManager will no longer register logger events with run or discovery events.
4. On test run/discovery completion, TestLoggerManager will be disposed by TestRunRequest and DiscoveryRequest.
5. Limitaion with this approach is that if TestRunRequestusers like TestRequestManager tries to call `TestRunRequest.ExecuteAsync()` second time, then logger events will not be invoked as TestLoggerManager will already be disposed in first run.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Limitaion/Limitation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/TestRunRequestusers


In reply to: 160961891 [](ancestors = 160961891)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting. What is the plan if we need this in future ?


In reply to: 160987392 [](ancestors = 160987392,160961891)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, we are supporting loggers for first test run of a test request. I will add a comment in code as well. It can come as a separate feature work if we add second run scenario.

* Disposing logger events.

2. TestPlatform will create and initialize TestLoggerManager and will pass this TestLoggerManager instance to TestRunRequest and DiscoveryRequest.
3. TestRunRequest and DiscoveryRequest will hold the TestLoggerManager instance. On receving any run/discovery events, respective methods of TestLoggerManager will be invoked. i.e. TestLoggerManager will no longer register logger events with run or discovery events.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

receving [](start = 84, length = 8)

s/receving/receiving

Copy link
Contributor

@singhsarab singhsarab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signing off with suggestions.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants