- Notifications
You must be signed in to change notification settings - Fork 89
Fixing multiple output windows bug #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
… was already static. However, all of the web editors code checks for pane existence prior to creating one. Not sure how it can exist if our static is null, but it appears to be possible. So checking for existence before creating seems to fix that. I still went ahead and added (ProjectName) suffix to messages. However, the current logging design is kinf of iffy. Existing Log was a essentially a static class, but wasn't declared as such because Mads wanted it to implement ILogger interface. So we were actually creating multiple instances of that essentially static class... Really weird. Anyway, I created PerProjectLogger that has instance data now, and converted Logger into the actual static class it was really supposed to be. So PerProjectLogger suffixes messages with project name, while static messages of the Logger don't... It doesn't look horrible, but a bit weird. We can discuss the design more. Below is example output restoring jquery in two different projects in the same solution. Restoring jquery@3.3.0... (WebApplication1) 1 libraries restored in 0.22 seconds Restoring jquery@3.3.0... (WebApplication3) 1 libraries restored in 0.07 seconds
| | ||
| public void Log(string message, LogLevel level) | ||
| { | ||
| Logger.Log($"{message} ({_projectName})", level); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_projectName can be null here if we fail to get projectName
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have a fallback string for a default if we can't resolve a project name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what the default project name would be. I think if it's null, we should just omit it. Anything else would look bad to the users. I'm not sure in which case project name would be null though - do you guys know of such cases?
Anyway - I added null check and will be using empty string for now if there is no project name - so the output would be as before this change.
| // Copyright (c) .NET Foundation. All rights reserved. | ||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
| | ||
| using EnvDTE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: usings aren't sorted correctly (System first)
jimmylewis left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, could you clean up the commit message to be more in line with good commit message practices?
| { | ||
| string cwd = Path.GetDirectoryName(configFilePath); | ||
| WorkingDirectory = cwd; | ||
| Logger = new PerProjectLogger(VsHelpers.GetProjectName(configFilePath)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use a DI pattern for the Logger to make this class more testable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, taking it as a parameter now and added internal setter as well.
| using Microsoft.Web.LibraryManager.Contracts; | ||
| using Microsoft.VisualStudio.Shell.Interop; | ||
| using System; | ||
| using Microsoft.VisualStudio; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: sort usings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleaned up.
| namespace Microsoft.Web.LibraryManager.Vsix | ||
| { | ||
| public class Logger : ILogger | ||
| public static class Logger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should chat about this. I prefer to avoid static classes if possible, they're hard to test. It would be better IMO if we had a mechanism to manage the lifetime of the instance (like an IoC container would provide).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also feels weird that Logger doesn't implement ILogger, but another type does and all it does is call methods on Logger. That seems like it ought to be using an inherited class instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think inheritance between PerProjectLogger and Logger would be appropriate here as "Logger" is essentially a VS Log Sink, just a wrapper around Status Bar & OutputWindow, and there will always be just one of them. PerProjectLogger gets created by the project, use by external "clients", and sends output to the VS Log Sink. So I think the composition is more appropriate here than inheritance.
As far as static objects vs singletons and making things testable, I totally agree in general. In this particular case, Logger is simply a very thin wrapper around VS Output Window and Status Bar, so I don't think it makes sense to test it via unit tests. I'll leave it as static for now, and we can chat more about it if you want.
| public class Logger : ILogger | ||
| public static class Logger | ||
| { | ||
| private static Guid _outputPaneGuid = new Guid("cce35aef-ace6-4371-b1e1-8efa3cdc8324"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be readonly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just kidding, can't be, passed by ref to a few methods below (even though they don't change it).
| | ||
| namespace Microsoft.Web.LibraryManager.Vsix.Contracts | ||
| { | ||
| internal class PerProjectLogger : ILogger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As commented on Logger, it feels like this ought to be extending the Logger class (before it was static). HostInteraction would get an instance of this type through DI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my comments above. We can chat more about it in person.
| | ||
| public void Log(string message, LogLevel level) | ||
| { | ||
| Logger.Log($"{message} ({_projectName})", level); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have a fallback string for a default if we can't resolve a project name?
| | ||
| IVsUIShellOpenDocument vsUIShellOpenDocument = ServiceProvider.GlobalProvider.GetService(typeof(SVsUIShellOpenDocument)) as IVsUIShellOpenDocument; | ||
| | ||
| IOleServiceProvider serviceProviderUnused = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you don't need this, you can just use out _ in the method call and it will be discarded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same with docInProject
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, didn't know about these, thanks!
… was already static. However, all of the web editors code checks for pane existence prior to creating one. Not sure how it can exist if our static is null, but it appears to be possible. So checking for existence before creating seems to fix that. I still went ahead and added (ProjectName) suffix to messages. However, the current logging design is kinf of iffy. Existing Log was a essentially a static class, but wasn't declared as such because Mads wanted it to implement ILogger interface. So we were actually creating multiple instances of that essentially static class... Really weird. Anyway, I created PerProjectLogger that has instance data now, and converted Logger into the actual static class it was really supposed to be. So PerProjectLogger suffixes messages with project name, while static messages of the Logger don't... It doesn't look horrible, but a bit weird. We can discuss the design more. Below is example output restoring jquery in two different projects in the same solution. Restoring jquery@3.3.0... (WebApplication1) 1 libraries restored in 0.22 seconds Restoring jquery@3.3.0... (WebApplication3) 1 libraries restored in 0.07 seconds
… was already static. However, all of the web editors code checks for pane existence prior to creating one. Not sure how it can exist if our static is null, but it appears to be possible. So checking for existence before creating seems to fix that.
I still went ahead and added (ProjectName) suffix to messages. However, the current logging design is kinf of iffy. Existing Log was a essentially a static class, but wasn't declared as such because Mads wanted it to implement ILogger interface. So we were actually creating multiple instances of that essentially static class... Really weird. Anyway, I created PerProjectLogger that has instance data now, and converted Logger into the actual static class it was really supposed to be. So PerProjectLogger suffixes messages with project name, while static messages of the Logger don't... It doesn't look horrible, but a bit weird. We can discuss the design more. Below is example output restoring jquery in two different projects in the same solution.
Restoring jquery@3.3.0... (WebApplication1)
1 libraries restored in 0.22 seconds
Restoring jquery@3.3.0... (WebApplication3)
1 libraries restored in 0.07 seconds