├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── Topper.sln
├── Topper
├── HostSettings.cs
├── Internals
│ ├── LibLogConfiguratorExtensions.cs
│ ├── LibLogLogWriter.cs
│ ├── LibLogLogWriterFactory.cs
│ ├── LibLogLoggerConfigurator.cs
│ ├── Service.cs
│ └── TopperService.cs
├── ServiceConfiguration.cs
├── ServiceHost.cs
├── Topper.csproj
└── Topper.xml
├── Toppertest
├── App.config
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── TestService1.cs
├── TestService2.cs
├── Toppertest.csproj
└── packages.config
├── appveyor.yml
├── scripts
├── build.cmd
├── patch_assemblyinfo.cmd
├── push.cmd
└── release.cmd
└── tools
├── NuGet
└── nuget.exe
└── aversion
└── Aversion.exe
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ---
2 | Rebus is [MIT-licensed](https://opensource.org/licenses/MIT). The code submitted in this pull request needs to carry the MIT license too. By leaving this text in, __I hereby acknowledge that the code submitted in the pull request has the MIT license and can be merged with the Rebus codebase__.
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | obj
2 | bin
3 | deploy
4 | deploy/*
5 | _ReSharper.*
6 | *.csproj.user
7 | *.resharper.user
8 | *.ReSharper.user
9 | *.teamcity.user
10 | *.TeamCity.user
11 | *.resharper
12 | *.DotSettings.user
13 | *.dotsettings.user
14 | *.ncrunchproject
15 | *.ncrunchsolution
16 | *.suo
17 | *.cache
18 | ~$*
19 | .vs
20 | .vs/*
21 | _NCrunch_*
22 | *.user
23 | *.backup
24 |
25 | # MS Guideline
26 | **/packages/*
27 | !**/packages/build/
28 | AssemblyInfo_Patch.cs
29 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.0.0
4 | * Initial version
5 |
6 | ## 1.0.1
7 | * Exception handling
8 |
9 | ## 2.0.0
10 | * Lose the Serilog dependency in favor of LibLog - thanks [scardetto]
11 |
12 | ## 2.1.0
13 | * Make it Azure Web Job-hostable
14 |
15 | ## 2.1.1
16 | * Fix it so that it works as Azure Web Job
17 |
18 | ## 2.1.2
19 | * Fix service name when calling Topshelf
20 |
21 | ## 2.1.3
22 | * Don't spam the logs with shutdown notifications
23 |
24 | ## 2.1.4
25 | * Make Azure Web Job shutdown logic log it if something goes wrong
26 |
27 | ## 3.0.0
28 | * Target .NET Standard 2.0
29 |
30 | ## 3.1.0
31 | * Add ability to parallelize initialization/disposal of services
32 | * Service add function now exists in overload that passes a `CancellationToken` to the asynchronous initialization function
33 | * Include XML docs
34 |
35 | ## 3.2.0
36 | * Provide ability to customize Topshelf's `HostConfigurator`, making it possible e.g. to configure a service's description, crash recovery, etc. - thanks [igitur]
37 |
38 | ## 3.2.1
39 | * Work around bug in Topshelf, which would not trigger Windows Service recovery if startup failed
40 |
41 | [igitur]: https://github.com/igitur
42 | [scardetto]: https://github.com/scardetto
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributions are most welcome! :)
2 |
3 | I do prefer it if we communicate a little bit before you send PRs, though.
4 | This is because _I value your time_, and it would be a shame if you spent time
5 | working on something that could be made better in another way, or wasn't
6 | actually needed because what you wanted to achieve could be done better in
7 | another way, etc.
8 |
9 | ## "Beginners"
10 |
11 | Contributions are ALSO very welcome if you consider yourself a beginner
12 | at open source. Everyone has to start somewhere, right?
13 |
14 | Here's how you would ideally do it if you were to contribute to Rebus:
15 |
16 | * Pick an [issue](https://github.com/rebus-org/Rebus/issues) you're interested in doing,
17 | or dream up something yourself that you feel is missing.
18 | * If you talk to me first (either via comments on the issue or by email), I
19 | will guarantee that your contribution is accepted.
20 | * Send me a "pull request" (which is how you make contributions on GitHub)
21 |
22 | ### Here's how you create a pull request/PR
23 |
24 | * Fork Rebus ([Fork A Repo @ GitHub docs](https://help.github.com/articles/fork-a-repo/))
25 | * Clone your fork ([Cloning A Repository @ GitHub docs](https://help.github.com/articles/cloning-a-repository/))
26 | * Make changes to your local copy (e.g. `git commit -am"bam!!!11"` - check [this](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) out for more info)
27 | * Push your local changes to your fork ([Pushing To A Remote @ GitHub docs](https://help.github.com/articles/pushing-to-a-remote/))
28 | * Send me a pull request ([Using Pull Requests @ GitHub docs](https://help.github.com/articles/using-pull-requests/))
29 |
30 | When you do this, your changes become visible to me. I can then review it, and we can discuss
31 | each line of code if necessary.
32 |
33 | If you push additional changes to your fork during this process,
34 | the changes become immediately available in the pull request.
35 |
36 | When all is good, I accept your PR by merging it, and then you're (even more) awesome!
37 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Rebus is licensed under [The MIT License (MIT)](http://opensource.org/licenses/MIT)
2 |
3 | # The MIT License (MIT)
4 |
5 | Copyright (c) 2012-2016 Mogens Heller Grabe
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | THE SOFTWARE.
24 |
25 | -------
26 |
27 | This license was chosen with the intention of making it easy for everyone to use Rebus. If the license has the opposite effect for your specific usage/organization/whatever, please contact me and we'll see if we can work something out.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Topper
2 |
3 | [](https://www.nuget.org/packages/Topper)
4 |
5 | Generic Windows service host - makes an ordinary Console Application hostable in the following scenarios:
6 |
7 | * To be F5-debugged locally - on your developer machine
8 | * To be installed as a Windows Service - on the servers in your basement
9 | * To be executed as an Azure Web Job - in the cloud!!
10 |
11 | Based on Topshelf. Exposes a drastically simplified API, where "services" are simply factories that return something `IDisposable`.
12 |
13 | Targets .NET Standard 2.0, so you must target either netcoreapp2.0 (or later), or net462 (or later) in your Console Application.
14 |
15 | ## Getting started
16 |
17 | Create `YourNewAwesomeWindowsService` as a Console Application project targeting AT LEAST .NET 4.6.2 or .NET Core App 2.0.
18 |
19 | Include the NuGet package :package:
20 |
21 | Install-Package Topper -ProjectName YourNewAwesomeWindowsService
22 |
23 | and clean up your `Program.cs` so it becomes nice like this: :sunflower:
24 |
25 | ```csharp
26 | namespace YourNewAwesomeWindowsService
27 | {
28 | class Program
29 | {
30 | static void Main()
31 | {
32 |
33 | }
34 | }
35 | }
36 | ```
37 | and then you configure Topper by going
38 |
39 | ```csharp
40 | var configuration = new ServiceConfiguration()
41 | .Add(.. function that returns an IDisposable ..)
42 | .Add(.. another function that returns an IDisposable ..);
43 |
44 | ServiceHost.Run(configuration);
45 | ```
46 |
47 | in `Main`, which could look like this:
48 |
49 | ```csharp
50 | namespace YourNewAwesomeWindowsService
51 | {
52 | class Program
53 | {
54 | static void Main()
55 | {
56 | var configuration = new ServiceConfiguration()
57 | .Add(() => new MyNewAwesomeService());
58 |
59 | ServiceHost.Run(configuration);
60 | }
61 | }
62 | }
63 | ```
64 |
65 | :monkey_face: Easy!
66 |
67 | Topper uses LibLog :zap: to log things. If you want to use Serilog, you probably want to
68 |
69 | ```psh
70 | Install-Package Serilog.Sinks.ColoredConsole -ProjectName YourNewAwesomeWindowsService
71 | ```
72 |
73 | and configure the global :earth_africa: logger before starting your service:
74 |
75 | ```csharp
76 | namespace YourNewAwesomeWindowsService
77 | {
78 | class Program
79 | {
80 | static void Main()
81 | {
82 | Log.Logger = new LoggerConfiguration()
83 | .WriteTo.ColoredConsole()
84 | .CreateLogger();
85 |
86 | var configuration = new ServiceConfiguration()
87 | .Add(() => new MyNewAwesomeService());
88 |
89 | ServiceHost.Run(configuration);
90 | }
91 | }
92 | }
93 | ```
94 |
95 |
96 | And that is how you use Topper.
97 |
98 | ## How to run locally?
99 |
100 | Press F5 or CTRL+F5 in Visual Studio.
101 |
102 | Run the .exe
103 |
104 | ## How to run as Windows Service?
105 |
106 | Open an elevated command prompt, and run the .exe with the `install` argument, like so:
107 |
108 | ```dos
109 | C:\apps\YourApp> YourApp.exe install
110 | ```
111 |
112 | and then some Windows Service Control :traffic_light: stuff will appear and tell you some details on how it was installed.
113 |
114 | You can remove it again like this:
115 |
116 | ```dos
117 | C:\apps\YourApp> YourApp.exe uninstall
118 | ```
119 |
120 | Not exactly surprising. :clap:
121 |
122 | ## How to run as Azure Web Job?
123 |
124 | Just run it as you would any other Console Application as a Continuous Web Job.
125 |
126 | Topper automatically monitors for the presence of the `WEBJOBS_SHUTDOWN_FILE`, to be able to shut down gracefully and dispose your `IDisposable`s. :recycle:
127 |
128 | ---
129 |
130 |
131 |
--------------------------------------------------------------------------------
/Topper.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio 15
3 | VisualStudioVersion = 15.0.26206.0
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stuff", "stuff", "{1ABDD7C1-1F8D-402D-8692-3E4B66962DE0}"
6 | ProjectSection(SolutionItems) = preProject
7 | CHANGELOG.md = CHANGELOG.md
8 | CONTRIBUTING.md = CONTRIBUTING.md
9 | LICENSE.md = LICENSE.md
10 | README.md = README.md
11 | EndProjectSection
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toppertest", "Toppertest\Toppertest.csproj", "{8600AE2B-14A1-49CA-9A0E-3BED87BA7BB1}"
14 | EndProject
15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Topper", "Topper\Topper.csproj", "{366585BE-FAD9-4141-8DA3-E1B9CBBB5E3A}"
16 | EndProject
17 | Global
18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 | Debug|Any CPU = Debug|Any CPU
20 | Release|Any CPU = Release|Any CPU
21 | EndGlobalSection
22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
23 | {8600AE2B-14A1-49CA-9A0E-3BED87BA7BB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {8600AE2B-14A1-49CA-9A0E-3BED87BA7BB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {8600AE2B-14A1-49CA-9A0E-3BED87BA7BB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {8600AE2B-14A1-49CA-9A0E-3BED87BA7BB1}.Release|Any CPU.Build.0 = Release|Any CPU
27 | {366585BE-FAD9-4141-8DA3-E1B9CBBB5E3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {366585BE-FAD9-4141-8DA3-E1B9CBBB5E3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
29 | {366585BE-FAD9-4141-8DA3-E1B9CBBB5E3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {366585BE-FAD9-4141-8DA3-E1B9CBBB5E3A}.Release|Any CPU.Build.0 = Release|Any CPU
31 | EndGlobalSection
32 | GlobalSection(SolutionProperties) = preSolution
33 | HideSolutionNode = FALSE
34 | EndGlobalSection
35 | GlobalSection(ExtensibilityGlobals) = postSolution
36 | SolutionGuid = {E5B632C3-64EE-4176-A7F7-F4FF93C58AA8}
37 | EndGlobalSection
38 | EndGlobal
39 |
--------------------------------------------------------------------------------
/Topper/HostSettings.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable RedundantDefaultMemberInitializer
2 | // ReSharper disable UnusedMember.Global
3 |
4 | using System;
5 | using Topshelf.HostConfigurators;
6 |
7 | namespace Topper
8 | {
9 | ///
10 | /// Represents additional settings
11 | ///
12 | public class HostSettings
13 | {
14 | internal bool ParallelStartup { get; set; } = false;
15 | internal bool ParallelShutdown { get; set; } = false;
16 |
17 | ///
18 | /// Optional Topshelf host configuration customizer
19 | ///
20 | Action _hostConfigurator;
21 |
22 | ///
23 | /// Enables parallel execution of service initializers. By default, services are serially initialized in the order in which they're added.
24 | /// Calling this method makes all initialization functions run in parallel.
25 | ///
26 | public void EnableParallelStartup()
27 | {
28 | ParallelStartup = true;
29 | }
30 |
31 | ///
32 | /// Enables parallel execution of service disposal. By default, services are serially disposed in the opposite order of which they're added.
33 | /// Calling this method makes all dispose functions run in parallel.
34 | ///
35 | public void EnableParallelShutdown()
36 | {
37 | ParallelShutdown = true;
38 | }
39 |
40 | ///
41 | /// Enables customization of Topshelf's by providing a callback, which will be invoked when the service is installed.
42 | ///
43 | public HostSettings Topshelf(Action hostConfigurator)
44 | {
45 | if (_hostConfigurator != null)
46 | {
47 | throw new InvalidOperationException("A Topshelf host configuration customization function has already been added - please make only one call to Topshelf");
48 | }
49 | _hostConfigurator = hostConfigurator;
50 | return this;
51 | }
52 |
53 | internal Action GetHostConfigurator() => _hostConfigurator;
54 | }
55 | }
--------------------------------------------------------------------------------
/Topper/Internals/LibLogConfiguratorExtensions.cs:
--------------------------------------------------------------------------------
1 | using Topshelf.HostConfigurators;
2 | using Topshelf.Logging;
3 |
4 | namespace Topper.Internals
5 | {
6 | static class LibLogConfiguratorExtensions
7 | {
8 | public static void UseLibLog(this HostConfigurator configurator)
9 | {
10 | HostLogger.UseLogger(new LibLogLoggerConfigurator());
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Topper/Internals/LibLogLogWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Topper.Logging;
3 | using Topshelf.Logging;
4 |
5 | namespace Topper.Internals
6 | {
7 | class LibLogLogWriter : LogWriter
8 | {
9 | // LibLog logger
10 | readonly ILog _logger;
11 |
12 | public LibLogLogWriter(string name)
13 | {
14 | // Get lib log logger
15 | _logger = LogProvider.GetLogger(name);
16 | }
17 | public void Log(LoggingLevel level, object obj)
18 | {
19 | // Map topshelf loglevel to liblog loglevel
20 | var logLevel = MapLogLevel(level);
21 |
22 | // Don't log anything if loglevel is not specified
23 | if (logLevel == null) return;
24 |
25 | _logger.Log(logLevel.Value, () => FormatObject(obj));
26 | }
27 |
28 | public void Log(LoggingLevel level, object obj, Exception exception)
29 | {
30 | // If exception is null, use log method without exception
31 | if (exception == null)
32 | {
33 | Log(level, obj);
34 | return;
35 | }
36 |
37 | // Map topshelf loglevel to liblog loglevel
38 | var logLevel = MapLogLevel(level);
39 |
40 | // Don't log anything if loglevel is not specified
41 | if (logLevel == null) return;
42 |
43 | _logger.Log(logLevel.Value, () => FormatObject(obj), exception);
44 | }
45 |
46 | public void Log(LoggingLevel level, LogWriterOutputProvider messageProvider)
47 | {
48 | Log(level, messageProvider());
49 | }
50 |
51 | public void LogFormat(LoggingLevel level, IFormatProvider formatProvider, string format, params object[] args)
52 | {
53 | Log(level, string.Format(formatProvider, format, args));
54 | }
55 |
56 | public void LogFormat(LoggingLevel level, string format, params object[] args)
57 | {
58 | Log(level, string.Format(format, args));
59 | }
60 |
61 | public void Debug(object obj)
62 | {
63 | Log(LoggingLevel.Debug, obj);
64 | }
65 |
66 | public void Debug(object obj, Exception exception)
67 | {
68 | Log(LoggingLevel.Debug, obj, exception);
69 | }
70 |
71 | public void Debug(LogWriterOutputProvider messageProvider)
72 | {
73 | Log(LoggingLevel.Debug, messageProvider);
74 | }
75 |
76 | public void DebugFormat(IFormatProvider formatProvider, string format, params object[] args)
77 | {
78 | LogFormat(LoggingLevel.Debug, formatProvider, format, args);
79 | }
80 |
81 | public void DebugFormat(string format, params object[] args)
82 | {
83 | LogFormat(LoggingLevel.Debug, format, args);
84 | }
85 |
86 | public void Info(object obj)
87 | {
88 | Log(LoggingLevel.Info, obj);
89 | }
90 |
91 | public void Info(object obj, Exception exception)
92 | {
93 | Log(LoggingLevel.Info, obj, exception);
94 | }
95 |
96 | public void Info(LogWriterOutputProvider messageProvider)
97 | {
98 | Log(LoggingLevel.Info, messageProvider);
99 | }
100 |
101 | public void InfoFormat(IFormatProvider formatProvider, string format, params object[] args)
102 | {
103 | LogFormat(LoggingLevel.Info, formatProvider, format, args);
104 | }
105 |
106 | public void InfoFormat(string format, params object[] args)
107 | {
108 | LogFormat(LoggingLevel.Info, format, args);
109 | }
110 |
111 | public void Warn(object obj)
112 | {
113 | Log(LoggingLevel.Warn, obj);
114 | }
115 |
116 | public void Warn(object obj, Exception exception)
117 | {
118 | Log(LoggingLevel.Warn, obj, exception);
119 | }
120 |
121 | public void Warn(LogWriterOutputProvider messageProvider)
122 | {
123 | Log(LoggingLevel.Warn, messageProvider);
124 | }
125 |
126 | public void WarnFormat(IFormatProvider formatProvider, string format, params object[] args)
127 | {
128 | LogFormat(LoggingLevel.Warn, formatProvider, format, args);
129 | }
130 |
131 | public void WarnFormat(string format, params object[] args)
132 | {
133 | LogFormat(LoggingLevel.Warn, format, args);
134 | }
135 |
136 | public void Error(object obj)
137 | {
138 | Log(LoggingLevel.Error, obj);
139 | }
140 |
141 | public void Error(object obj, Exception exception)
142 | {
143 | Log(LoggingLevel.Error, obj, exception);
144 | }
145 |
146 | public void Error(LogWriterOutputProvider messageProvider)
147 | {
148 | Log(LoggingLevel.Error, messageProvider);
149 | }
150 |
151 | public void ErrorFormat(IFormatProvider formatProvider, string format, params object[] args)
152 | {
153 | LogFormat(LoggingLevel.Error, formatProvider, format, args);
154 | }
155 |
156 | public void ErrorFormat(string format, params object[] args)
157 | {
158 | LogFormat(LoggingLevel.Error, format, args);
159 | }
160 |
161 | public void Fatal(object obj)
162 | {
163 | Log(LoggingLevel.Fatal, obj);
164 | }
165 |
166 | public void Fatal(object obj, Exception exception)
167 | {
168 | Log(LoggingLevel.Fatal, obj, exception);
169 | }
170 |
171 | public void Fatal(LogWriterOutputProvider messageProvider)
172 | {
173 | Log(LoggingLevel.Fatal, messageProvider);
174 | }
175 |
176 | public void FatalFormat(IFormatProvider formatProvider, string format, params object[] args)
177 | {
178 | LogFormat(LoggingLevel.Fatal, formatProvider, format, args);
179 | }
180 |
181 | public void FatalFormat(string format, params object[] args)
182 | {
183 | LogFormat(LoggingLevel.Fatal, format, args);
184 | }
185 |
186 | public bool IsDebugEnabled { get { return _logger.IsDebugEnabled(); } }
187 | public bool IsInfoEnabled { get { return _logger.IsInfoEnabled(); } }
188 | public bool IsWarnEnabled { get { return _logger.IsWarnEnabled(); } }
189 | public bool IsErrorEnabled { get { return _logger.IsErrorEnabled(); } }
190 | public bool IsFatalEnabled { get { return _logger.IsFatalEnabled(); } }
191 |
192 | private static string FormatObject(object obj)
193 | {
194 | return obj == null ? "" : obj.ToString();
195 | }
196 |
197 | private static LogLevel? MapLogLevel(LoggingLevel loglevel)
198 | {
199 | if (loglevel == LoggingLevel.Fatal)
200 | return LogLevel.Fatal;
201 | if (loglevel == LoggingLevel.Error)
202 | return LogLevel.Error;
203 | if (loglevel == LoggingLevel.Warn)
204 | return LogLevel.Warn;
205 | if (loglevel == LoggingLevel.Info)
206 | return LogLevel.Info;
207 | if (loglevel == LoggingLevel.Debug)
208 | return LogLevel.Debug;
209 | if (loglevel == LoggingLevel.All)
210 | return LogLevel.Trace;
211 |
212 | // Topshelf supports LogLevel None, which is not supported by LibLog
213 | // We return null, so the log methods can check for null and decide not to log anything
214 | return null;
215 | }
216 | }
217 | }
--------------------------------------------------------------------------------
/Topper/Internals/LibLogLogWriterFactory.cs:
--------------------------------------------------------------------------------
1 | using Topshelf.Logging;
2 |
3 | namespace Topper.Internals
4 | {
5 | class LibLogLogWriterFactory : LogWriterFactory
6 | {
7 | public LogWriter Get(string name)
8 | {
9 | return new LibLogLogWriter(name);
10 | }
11 |
12 | public void Shutdown()
13 | {
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/Topper/Internals/LibLogLoggerConfigurator.cs:
--------------------------------------------------------------------------------
1 | using Topshelf.Logging;
2 |
3 | namespace Topper.Internals
4 | {
5 | class LibLogLoggerConfigurator : HostLoggerConfigurator
6 | {
7 | public LogWriterFactory CreateLogWriterFactory()
8 | {
9 | return new LibLogLogWriterFactory();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Topper/Internals/Service.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Topper.Logging;
5 |
6 | namespace Topper.Internals
7 | {
8 | class Service : IDisposable
9 | {
10 | public string Name { get; }
11 |
12 | readonly Func> _function;
13 | readonly ILog _logger = LogProvider.GetCurrentClassLogger();
14 |
15 | IDisposable _disposable;
16 |
17 | volatile Task _initializationTask;
18 |
19 | public Service(Func> function, string name)
20 | {
21 | _function = function;
22 | Name = name;
23 | }
24 |
25 | public async Task Initialize(CancellationToken cancellationToken)
26 | {
27 | _logger.Debug($"Initializing service {Name}");
28 |
29 | _initializationTask = _function(cancellationToken);
30 | _disposable = await _initializationTask;
31 | }
32 |
33 | public void Dispose()
34 | {
35 | // if the initialization task is null, this service was never initialized... therefore, there's not dispoable to dispose either
36 | if (_initializationTask == null) return;
37 |
38 | if (!_initializationTask.Wait(TimeSpan.FromSeconds(10)))
39 | {
40 | _logger.Warn($"Service {Name} was disposed before initialization finished, and initialization did not finish within 10s timeout");
41 | }
42 |
43 | _disposable?.Dispose();
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/Topper/Internals/TopperService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Linq;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using Topper.Logging;
7 |
8 | namespace Topper.Internals
9 | {
10 | class TopperService
11 | {
12 | readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
13 | readonly ConcurrentStack _services = new ConcurrentStack();
14 | readonly ILog _logger = LogProvider.GetCurrentClassLogger();
15 | readonly ServiceConfiguration _configuration;
16 |
17 | public TopperService(ServiceConfiguration configuration)
18 | {
19 | _configuration = configuration;
20 |
21 | StartupFailed += _ => _cancellationTokenSource.Cancel();
22 | }
23 |
24 | HostSettings Settings => _configuration.GetSettings();
25 |
26 | public event Action StartupFailed;
27 |
28 | public void Start()
29 | {
30 | Task.Run(async () =>
31 | {
32 | try
33 | {
34 | await StartServices(_cancellationTokenSource.Token);
35 | }
36 | catch (Exception exception)
37 | {
38 | StartupFailed?.Invoke(exception);
39 | }
40 | });
41 | }
42 |
43 | async Task StartServices(CancellationToken cancellationToken)
44 | {
45 | var functions = _configuration.GetFunctions();
46 |
47 | if (Settings.ParallelStartup)
48 | {
49 | _logger.Info("Starting Topper service (parallel startup activated)");
50 | await Task.WhenAll(functions.Select(service => StartService(service, cancellationToken)));
51 | return;
52 | }
53 |
54 | foreach (var service in functions)
55 | {
56 | _logger.Info("Starting Topper service");
57 | await StartService(service, cancellationToken);
58 | }
59 | }
60 |
61 | public void Stop()
62 | {
63 | if (Settings.ParallelShutdown)
64 | {
65 | _logger.Info("Stopping Topper service (parallel shutdown activated)");
66 | Parallel.ForEach(_services, service =>
67 | {
68 | _logger.Debug($"Stopping service {service.Name}");
69 |
70 | service.Dispose();
71 | });
72 | return;
73 | }
74 |
75 | _logger.Info("Stopping Topper service");
76 |
77 | while (_services.TryPop(out var service))
78 | {
79 | _logger.Debug($"Stopping service {service.Name}");
80 |
81 | service.Dispose();
82 | }
83 | }
84 |
85 | async Task StartService(Service service, CancellationToken cancellationToken)
86 | {
87 | try
88 | {
89 | _services.Push(service);
90 |
91 | _logger.Debug($"Starting service {service.Name}");
92 |
93 | await service.Initialize(cancellationToken);
94 | }
95 | catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
96 | {
97 | _logger.Debug($"Service {service.Name} startup cancelled");
98 | }
99 | catch (Exception exception)
100 | {
101 | throw new ApplicationException($"Could not start service '{service.Name}'", exception);
102 | }
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/Topper/ServiceConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using Topper.Internals;
6 | using Topshelf.HostConfigurators;
7 | // ReSharper disable UnusedMember.Global
8 | // ReSharper disable EmptyConstructor
9 | #pragma warning disable 1998
10 |
11 | namespace Topper
12 | {
13 | ///
14 | /// Create an instance of this one and add services to it by calling ,
15 | /// , or
16 | /// .
17 | /// When you have added enough services to it, call with the configuration
18 | ///
19 | public class ServiceConfiguration
20 | {
21 | readonly List _serviceFunctions = new List();
22 | readonly HostSettings _hostSettings = new HostSettings();
23 |
24 |
25 | ///
26 | /// Creates the service configuration object. Start out by calling this, then add services to it, then call
27 | /// with it.
28 | ///
29 | public ServiceConfiguration()
30 | {
31 | }
32 |
33 | ///
34 | /// Invokes configuration callback that makes it possible to further customize things
35 | ///
36 | public ServiceConfiguration Configure(Action customizeHostSettings)
37 | {
38 | if (customizeHostSettings == null) throw new ArgumentNullException(nameof(customizeHostSettings));
39 | customizeHostSettings(_hostSettings);
40 | return this;
41 | }
42 |
43 | ///
44 | /// Adds the given service function with the given name
45 | ///
46 | public ServiceConfiguration Add(string name, Func serviceFunction)
47 | {
48 | _serviceFunctions.Add(new Service(async _ => serviceFunction(), name));
49 | return this;
50 | }
51 |
52 | ///
53 | /// Adds the given async service function with the given name
54 | ///
55 | public ServiceConfiguration Add(string name, Func> serviceFunction)
56 | {
57 | _serviceFunctions.Add(new Service(_ => serviceFunction(), name));
58 | return this;
59 | }
60 |
61 | ///
62 | /// Adds the given async service function with the given name, passing a cancellation token in
63 | ///
64 | public ServiceConfiguration Add(string name, Func> serviceFunction)
65 | {
66 | _serviceFunctions.Add(new Service(serviceFunction, name));
67 | return this;
68 | }
69 |
70 | internal HostSettings GetSettings() => _hostSettings;
71 |
72 | internal IEnumerable GetFunctions() => _serviceFunctions;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Topper/ServiceHost.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.IO;
4 | using System.Reflection;
5 | using System.Threading;
6 | using Topper.Internals;
7 | using Topper.Logging;
8 | using Topshelf;
9 | using Timer = System.Timers.Timer;
10 |
11 | namespace Topper
12 | {
13 | ///
14 | /// Call the method to start the host
15 | ///
16 | public static class ServiceHost
17 | {
18 | static readonly ILog Log = LogProvider.GetLogger(typeof(ServiceHost));
19 | static readonly ConcurrentStack Disposables = new ConcurrentStack();
20 |
21 | ///
22 | /// Starts the service host with the given
23 | ///
24 | public static void Run(ServiceConfiguration configuration)
25 | {
26 | if (configuration == null) throw new ArgumentNullException(nameof(configuration));
27 |
28 | if (IsAzureWebJob)
29 | {
30 | RunAsAzureWebJob(configuration);
31 | return;
32 | }
33 |
34 | RunAsTopShelf(configuration);
35 | }
36 |
37 | static void RunAsAzureWebJob(ServiceConfiguration configuration)
38 | {
39 | try
40 | {
41 | var topperService = new TopperService(configuration);
42 | var keepRunning = true;
43 |
44 | topperService.StartupFailed += exception =>
45 | {
46 | Log.ErrorException("Startup failed", exception);
47 | Volatile.Write(ref keepRunning, false);
48 | };
49 |
50 | DetectShutdownInAzureWebJobs(() =>
51 | {
52 | Volatile.Write(ref keepRunning, false);
53 | });
54 |
55 | try
56 | {
57 | Log.Info("Starting topper service(s)");
58 | topperService.Start();
59 |
60 | Log.Info("Running...");
61 |
62 | while (Volatile.Read(ref keepRunning))
63 | {
64 | Thread.Sleep(100);
65 | }
66 |
67 | Log.Info("Exiting...");
68 | }
69 | finally
70 | {
71 | Log.Info("Stopping topper service(s)");
72 | topperService.Stop();
73 | }
74 | }
75 | catch (Exception exception)
76 | {
77 | Log.ErrorException("Unhandled exception in", exception);
78 | }
79 | }
80 |
81 | static void RunAsTopShelf(ServiceConfiguration configuration)
82 | {
83 | var name = Assembly.GetEntryAssembly()?.GetName().Name ?? "(unknown name)";
84 |
85 | HostFactory.Run(factory =>
86 | {
87 | factory.SetServiceName(name);
88 | factory.UseLibLog();
89 | factory.OnException(exception => { Log.ErrorException("Unhandled exception", exception); });
90 | factory.Service(config =>
91 | {
92 | config.WhenStarted((service, control) =>
93 | {
94 | service.StartupFailed += exception =>
95 | {
96 | Log.ErrorException("Startup failed", exception);
97 |
98 | // bug in Topshelf: it doesn't seem to trigger recovery, when calling this:
99 | // control.Stop(TopshelfExitCode.AbnormalExit);
100 | // so instead we call this:
101 | Environment.Exit(-1);
102 | // more info here:
103 | // https://github.com/Topshelf/Topshelf/issues/479
104 | };
105 | service.Start();
106 | return true;
107 | });
108 | config.WhenStopped(service => service.Stop());
109 | config.ConstructUsing(() => new TopperService(configuration));
110 | });
111 |
112 | var hostConfiguratorOrNull = configuration.GetSettings().GetHostConfigurator();
113 |
114 | hostConfiguratorOrNull?.Invoke(factory);
115 | });
116 | }
117 |
118 | static bool IsAzureWebJob => !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("WEBJOBS_SHUTDOWN_FILE"));
119 |
120 | static void DetectShutdownInAzureWebJobs(Action stopAction)
121 | {
122 | var filePath = Environment.GetEnvironmentVariable("WEBJOBS_SHUTDOWN_FILE");
123 |
124 | if (string.IsNullOrWhiteSpace(filePath))
125 | {
126 | // not an Azure Web Job
127 | return;
128 | }
129 |
130 | Log.Info($"Will monitor for Azure Web Job shutdown file at {filePath}");
131 |
132 | var didAlreadySignalShutDown = false;
133 |
134 | var timer = Using(new Timer(300));
135 |
136 | timer.Elapsed += (o, ea) =>
137 | {
138 | try
139 | {
140 | if (!File.Exists(filePath)) return;
141 | if (didAlreadySignalShutDown) return;
142 |
143 | Log.Info($"Detected Azure Web Job file {filePath} - shutting down");
144 |
145 | try
146 | {
147 | stopAction();
148 | }
149 | catch (Exception exception)
150 | {
151 | Log.ErrorException("An error occurred when invoking shutdown callback", exception);
152 | }
153 | finally
154 | {
155 | didAlreadySignalShutDown = true;
156 | }
157 | }
158 | catch { }
159 | };
160 | timer.Start();
161 | }
162 |
163 | static TDisposable Using(TDisposable disposable) where TDisposable : IDisposable
164 | {
165 | Disposables.Push(disposable);
166 | return disposable;
167 | }
168 | }
169 | }
--------------------------------------------------------------------------------
/Topper/Topper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 | C:\projects-rebusfm\Topper\Topper\Topper.xml
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Topper/Topper.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Topper
5 |
6 |
7 |
8 |
9 | Represents additional settings
10 |
11 |
12 |
13 |
14 | Enables parallel execution of service initializers. By default, services are serially initialized in the order in which they're added.
15 | Calling this method makes all initialization functions run in parallel.
16 |
17 |
18 |
19 |
20 | Enables parallel execution of service disposal. By default, services are serially disposed in the opposite order of which they're added.
21 | Calling this method makes all dispose functions run in parallel.
22 |
23 |
24 |
25 |
26 | Create an instance of this one and add services to it by calling ,
27 | , or
28 | .
29 | When you have added enough services to it, call with the configuration
30 |
31 |
32 |
33 |
34 | Creates the service configuration object. Start out by calling this, then add services to it, then call
35 | with it.
36 |
37 |
38 |
39 |
40 | Invokes configuration callback that makes it possible to further customize things
41 |
42 |
43 |
44 |
45 | Adds the given service function with the given name
46 |
47 |
48 |
49 |
50 | Adds the given async service function with the given name
51 |
52 |
53 |
54 |
55 | Adds the given async service function with the given name, passing a cancellation token in
56 |
57 |
58 |
59 |
60 | Call the method to start the host
61 |
62 |
63 |
64 |
65 | Starts the service host with the given
66 |
67 |
68 |
69 |
70 | Simple interface that represent a logger.
71 |
72 |
73 |
74 |
75 | Log a message the specified log level.
76 |
77 | The log level.
78 | The message function.
79 | An optional exception.
80 | Optional format parameters for the message generated by the messagefunc.
81 | true if the message was logged. Otherwise false.
82 |
83 | Note to implementers: the message func should not be called if the loglevel is not enabled
84 | so as not to incur performance penalties.
85 | To check IsEnabled call Log with only LogLevel and check the return value, no event will be written.
86 |
87 |
88 |
89 |
90 | Represents a way to get a
91 |
92 |
93 |
94 |
95 | Gets the specified named logger.
96 |
97 | Name of the logger.
98 | The logger reference.
99 |
100 |
101 |
102 | Opens a nested diagnostics context. Not supported in EntLib logging.
103 |
104 | The message to add to the diagnostics context.
105 | A disposable that when disposed removes the message from the context.
106 |
107 |
108 |
109 | Opens a mapped diagnostics context. Not supported in EntLib logging.
110 |
111 | A key.
112 | A value.
113 | Determines whether to call the destructor or not.
114 | A disposable that when disposed removes the map from the context.
115 |
116 |
117 |
118 | Extension methods for the interface.
119 |
120 |
121 |
122 |
123 | Check if the log level is enabled.
124 |
125 | The to check with.
126 | True if the log level is enabled; false otherwise.
127 |
128 |
129 |
130 | Check if the log level is enabled.
131 |
132 | The to check with.
133 | True if the log level is enabled; false otherwise.
134 |
135 |
136 |
137 | Check if the log level is enabled.
138 |
139 | The to check with.
140 | True if the log level is enabled; false otherwise.
141 |
142 |
143 |
144 | Check if the log level is enabled.
145 |
146 | The to check with.
147 | True if the log level is enabled; false otherwise.
148 |
149 |
150 |
151 | Check if the log level is enabled.
152 |
153 | The to check with.
154 | True if the log level is enabled; false otherwise.
155 |
156 |
157 |
158 | Check if the log level is enabled.
159 |
160 | The to check with.
161 | True if the log level is enabled; false otherwise.
162 |
163 |
164 |
165 | Logs a message at the log level, if enabled.
166 |
167 | The to use.
168 | The message function.
169 |
170 |
171 |
172 | Logs a message at the log level, if enabled.
173 |
174 | The to use.
175 | The message.
176 |
177 |
178 |
179 | Logs a message at the log level, if enabled.
180 |
181 | The to use.
182 | The message.
183 | Optional format parameters for the message.
184 |
185 |
186 |
187 | Logs an exception at the log level, if enabled.
188 |
189 | The to use.
190 | The exception.
191 | The message.
192 | Optional format parameters for the message.
193 |
194 |
195 |
196 | Logs a message at the log level, if enabled.
197 |
198 | The to use.
199 | The message.
200 | Optional format parameters for the message.
201 |
202 |
203 |
204 | Logs an exception at the log level, if enabled.
205 |
206 | The to use.
207 | The exception.
208 | The message.
209 |
210 |
211 |
212 | Logs an exception at the log level, if enabled.
213 |
214 | The to use.
215 | The exception.
216 | The message.
217 | Optional format parameters for the message.
218 |
219 |
220 |
221 | Logs a message at the log level, if enabled.
222 |
223 | The to use.
224 | The message function.
225 |
226 |
227 |
228 | Logs a message at the log level, if enabled.
229 |
230 | The to use.
231 | The message.
232 |
233 |
234 |
235 | Logs a message at the log level, if enabled.
236 |
237 | The to use.
238 | The message.
239 | Optional format parameters for the message.
240 |
241 |
242 |
243 | Logs an exception at the log level, if enabled.
244 |
245 | The to use.
246 | The exception.
247 | The message.
248 | Optional format parameters for the message.
249 |
250 |
251 |
252 | Logs a message at the log level, if enabled.
253 |
254 | The to use.
255 | The message.
256 | Optional format parameters for the message.
257 |
258 |
259 |
260 | Logs an exception at the log level, if enabled.
261 |
262 | The to use.
263 | The exception.
264 | The message.
265 | Optional format parameters for the message.
266 |
267 |
268 |
269 | Logs a message at the log level, if enabled.
270 |
271 | The to use.
272 | The message function.
273 |
274 |
275 |
276 | Logs a message at the log level, if enabled.
277 |
278 | The to use.
279 | The message.
280 |
281 |
282 |
283 | Logs a message at the log level, if enabled.
284 |
285 | The to use.
286 | The message.
287 | Optional format parameters for the message.
288 |
289 |
290 |
291 | Logs an exception at the log level, if enabled.
292 |
293 | The to use.
294 | The exception.
295 | The message.
296 | Optional format parameters for the message.
297 |
298 |
299 |
300 | Logs a message at the log level, if enabled.
301 |
302 | The to use.
303 | The message.
304 | Optional format parameters for the message.
305 |
306 |
307 |
308 | Logs an exception at the log level, if enabled.
309 |
310 | The to use.
311 | The exception.
312 | The message.
313 | Optional format parameters for the message.
314 |
315 |
316 |
317 | Logs a message at the log level, if enabled.
318 |
319 | The to use.
320 | The message function.
321 |
322 |
323 |
324 | Logs a message at the log level, if enabled.
325 |
326 | The to use.
327 | The message.
328 |
329 |
330 |
331 | Logs a message at the log level, if enabled.
332 |
333 | The to use.
334 | The message.
335 | Optional format parameters for the message.
336 |
337 |
338 |
339 | Logs an exception at the log level, if enabled.
340 |
341 | The to use.
342 | The exception.
343 | The message.
344 | Optional format parameters for the message.
345 |
346 |
347 |
348 | Logs a message at the log level, if enabled.
349 |
350 | The to use.
351 | The message.
352 | Optional format parameters for the message.
353 |
354 |
355 |
356 | Logs an exception at the log level, if enabled.
357 |
358 | The to use.
359 | The exception.
360 | The message.
361 | Optional format parameters for the message.
362 |
363 |
364 |
365 | Logs a message at the log level, if enabled.
366 |
367 | The to use.
368 | The message function.
369 |
370 |
371 |
372 | Logs a message at the log level, if enabled.
373 |
374 | The to use.
375 | The message.
376 |
377 |
378 |
379 | Logs a message at the log level, if enabled.
380 |
381 | The to use.
382 | The message.
383 | Optional format parameters for the message.
384 |
385 |
386 |
387 | Logs an exception at the log level, if enabled.
388 |
389 | The to use.
390 | The exception.
391 | The message.
392 | Optional format parameters for the message.
393 |
394 |
395 |
396 | Logs a message at the log level, if enabled.
397 |
398 | The to use.
399 | The message.
400 | Optional format parameters for the message.
401 |
402 |
403 |
404 | Logs an exception at the log level, if enabled.
405 |
406 | The to use.
407 | The exception.
408 | The message.
409 | Optional format parameters for the message.
410 |
411 |
412 |
413 | Logs a message at the log level, if enabled.
414 |
415 | The to use.
416 | The message function.
417 |
418 |
419 |
420 | Logs a message at the log level, if enabled.
421 |
422 | The to use.
423 | The message.
424 |
425 |
426 |
427 | Logs a message at the log level, if enabled.
428 |
429 | The to use.
430 | The message.
431 | Optional format parameters for the message.
432 |
433 |
434 |
435 | Logs an exception at the log level, if enabled.
436 |
437 | The to use.
438 | The exception.
439 | The message.
440 | Optional format parameters for the message.
441 |
442 |
443 |
444 | Logs a message at the log level, if enabled.
445 |
446 | The to use.
447 | The message.
448 | Optional format parameters for the message.
449 |
450 |
451 |
452 | Logs an exception at the log level, if enabled.
453 |
454 | The to use.
455 | The exception.
456 | The message.
457 | Optional format parameters for the message.
458 |
459 |
460 |
461 | The log level.
462 |
463 |
464 |
465 |
466 | Trace
467 |
468 |
469 |
470 |
471 | Debug
472 |
473 |
474 |
475 |
476 | Info
477 |
478 |
479 |
480 |
481 | Warn
482 |
483 |
484 |
485 |
486 | Error
487 |
488 |
489 |
490 |
491 | Fatal
492 |
493 |
494 |
495 |
496 | Provides a mechanism to create instances of objects.
497 |
498 |
499 |
500 |
501 | Sets the current log provider.
502 |
503 | The log provider.
504 |
505 |
506 |
507 | Gets or sets a value indicating whether this is logging is disabled.
508 |
509 |
510 | true if logging is disabled; otherwise, false.
511 |
512 |
513 |
514 |
515 | Sets an action that is invoked when a consumer of your library has called SetCurrentLogProvider. It is
516 | important that hook into this if you are using child libraries (especially ilmerged ones) that are using
517 | LibLog (or other logging abstraction) so you adapt and delegate to them.
518 |
519 |
520 |
521 |
522 |
523 | Gets a logger for the specified type.
524 |
525 | The type whose name will be used for the logger.
526 | An instance of
527 |
528 |
529 |
530 | Gets a logger for the current class.
531 |
532 | An instance of
533 |
534 |
535 |
536 | Gets a logger for the specified type.
537 |
538 | The type whose name will be used for the logger.
539 | If the type is null then this name will be used as the log name instead
540 | An instance of
541 |
542 |
543 |
544 | Gets a logger with the specified name.
545 |
546 | The name.
547 | An instance of
548 |
549 |
550 |
551 | Opens a nested diagnostics context.
552 |
553 | A message.
554 | An that closes context when disposed.
555 |
556 |
557 |
558 | Opens a mapped diagnostics context.
559 |
560 | A key.
561 | A value.
562 | A optional paramater to indicate message should be destructured.
563 | An that closes context when disposed.
564 |
565 |
566 |
567 | Exception thrown by LibLog.
568 |
569 |
570 |
571 |
572 | Initializes a new LibLogException with the specified message.
573 |
574 | The message
575 |
576 |
577 |
578 | Initializes a new LibLogException with the specified message and inner exception.
579 |
580 | The message.
581 | The inner exception.
582 |
583 |
584 |
585 | Some logging frameworks support structured logging, such as serilog. This will allow you to add names to structured
586 | data in a format string:
587 | For example: Log("Log message to {user}", user). This only works with serilog, but as the user of LibLog, you don't
588 | know if serilog is actually
589 | used. So, this class simulates that. it will replace any text in {curly braces} with an index number.
590 | "Log {message} to {user}" would turn into => "Log {0} to {1}". Then the format parameters are handled using regular
591 | .net string.Format.
592 |
593 | The message builder.
594 | The format parameters.
595 |
596 |
597 |
598 |
599 | Base class for specific log providers.
600 |
601 |
602 |
603 |
604 | Error message should initializing the log provider fail.
605 |
606 |
607 |
608 |
609 | Initialize an instance of the class by initializing the references
610 | to the nested and mapped diagnostics context-obtaining functions.
611 |
612 |
613 |
614 |
615 | Gets the specified named logger.
616 |
617 | Name of the logger.
618 | The logger reference.
619 |
620 |
621 |
622 | Opens a nested diagnostics context. Not supported in EntLib logging.
623 |
624 | The message to add to the diagnostics context.
625 | A disposable that when disposed removes the message from the context.
626 |
627 |
628 |
629 | Opens a mapped diagnostics context. Not supported in EntLib logging.
630 |
631 | A key.
632 | A value.
633 | Determines whether to call the destructor or not.
634 | A disposable that when disposed removes the map from the context.
635 |
636 |
637 |
638 | Returns the provider-specific method to open a nested diagnostics context.
639 |
640 | A provider-specific method to open a nested diagnostics context.
641 |
642 |
643 |
644 | Returns the provider-specific method to open a mapped diagnostics context.
645 |
646 | A provider-specific method to open a mapped diagnostics context.
647 |
648 |
649 |
650 | Delegate defining the signature of the method opening a nested diagnostics context.
651 |
652 | The message to add to the diagnostics context.
653 | A disposable that when disposed removes the message from the context.
654 |
655 |
656 |
657 | Delegate defining the signature of the method opening a mapped diagnostics context.
658 |
659 | A key.
660 | A value.
661 | Determines whether to call the destructor or not.
662 | A disposable that when disposed removes the map from the context.
663 |
664 |
665 |
666 | The form of the Loupe Log.Write method we're using
667 |
668 |
669 |
670 |
671 | Gets or sets a value indicating whether [provider is available override]. Used in tests.
672 |
673 |
674 | true if [provider is available override]; otherwise, false.
675 |
676 |
677 |
678 |
679 | Logger delegate.
680 |
681 | The log level
682 | The message function
683 | The exception
684 | The format parameters
685 | A boolean.
686 |
687 |
688 |
689 |
--------------------------------------------------------------------------------
/Toppertest/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Toppertest/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Serilog;
4 | using Topper;
5 | using Topshelf;
6 |
7 | namespace Toppertest
8 | {
9 | class Program
10 | {
11 | static void Main()
12 | {
13 | Log.Logger = new LoggerConfiguration()
14 | .WriteTo.ColoredConsole()
15 | .WriteTo.File(@"C:\logs\toppertest\log.txt", rollOnFileSizeLimit: true, fileSizeLimitBytes: 1024 * 1024)
16 | .MinimumLevel.Verbose()
17 | .CreateLogger();
18 |
19 | var configuration = new ServiceConfiguration()
20 | .Configure(c =>
21 | {
22 | //c.EnableParallelStartup();
23 | //c.EnableParallelShutdown();
24 |
25 | c.Topshelf(config =>
26 | {
27 | config.SetDescription("Some description");
28 | config.EnableServiceRecovery(r => r.RestartService(5));
29 | });
30 | })
31 | .Add("crashtest", async () =>
32 | {
33 | var service = new CrashingTestService();
34 | await service.CrashAsync();
35 | return service;
36 | })
37 | //.Add("test1", () => new TestService1())
38 | //.Add("test2", () => new TestService2())
39 | ;
40 |
41 | ServiceHost.Run(configuration);
42 | }
43 | }
44 |
45 | class CrashingTestService : IDisposable
46 | {
47 | public async Task CrashAsync()
48 | {
49 | await Task.Delay(TimeSpan.FromSeconds(2));
50 |
51 | throw new InvalidOperationException("OH NO!");
52 | }
53 |
54 | public void Dispose()
55 | {
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Toppertest/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Toppertest")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Toppertest")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("8600ae2b-14a1-49ca-9a0e-3bed87ba7bb1")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Toppertest/TestService1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Toppertest
4 | {
5 | class TestService1 : IDisposable
6 | {
7 | public TestService1()
8 | {
9 | }
10 |
11 | public void Dispose()
12 | {
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/Toppertest/TestService2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Toppertest
4 | {
5 | class TestService2 : IDisposable
6 | {
7 | public TestService2()
8 | {
9 | }
10 | public void Dispose()
11 | {
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/Toppertest/Toppertest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {8600AE2B-14A1-49CA-9A0E-3BED87BA7BB1}
8 | Exe
9 | Toppertest
10 | Toppertest
11 | v4.6.2
12 | 512
13 | true
14 |
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\Serilog.2.9.0\lib\net46\Serilog.dll
38 |
39 |
40 | ..\packages\Serilog.Sinks.ColoredConsole.3.0.1\lib\net45\Serilog.Sinks.ColoredConsole.dll
41 |
42 |
43 | ..\packages\Serilog.Sinks.Console.3.1.1\lib\net45\Serilog.Sinks.Console.dll
44 |
45 |
46 | ..\packages\Serilog.Sinks.File.4.1.0\lib\net45\Serilog.Sinks.File.dll
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | ..\packages\Topshelf.4.2.1\lib\net452\Topshelf.dll
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | {366585BE-FAD9-4141-8DA3-E1B9CBBB5E3A}
75 | Topper
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Toppertest/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | before_build:
2 | - nuget restore
3 |
4 |
--------------------------------------------------------------------------------
/scripts/build.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | set scriptsdir=%~dp0
4 | set root=%scriptsdir%\..
5 | set project=%1
6 | set version=%2
7 |
8 | if "%project%"=="" (
9 | echo Please invoke the build script with a project name as its first argument.
10 | echo.
11 | goto exit_fail
12 | )
13 |
14 | if "%version%"=="" (
15 | echo Please invoke the build script with a version as its second argument.
16 | echo.
17 | goto exit_fail
18 | )
19 |
20 | set Version=%version%
21 |
22 | pushd %root%
23 |
24 | dotnet restore
25 | if %ERRORLEVEL% neq 0 (
26 | popd
27 | goto exit_fail
28 | )
29 |
30 | dotnet build "%root%\%project%" -c Release
31 | if %ERRORLEVEL% neq 0 (
32 | popd
33 | goto exit_fail
34 | )
35 |
36 | popd
37 |
38 |
39 |
40 |
41 |
42 |
43 | goto exit_success
44 | :exit_fail
45 | exit /b 1
46 | :exit_success
--------------------------------------------------------------------------------
/scripts/patch_assemblyinfo.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | set reporoot=%~dp0\..
4 | set aversion=%reporoot%\tools\aversion\aversion
5 | set projectdir=%1
6 |
7 | if "%version%"=="" (
8 | "%aversion%" patch -ver 1.0.0 -in %projectdir%\Properties\AssemblyInfo.cs -out %projectdir%\Properties\AssemblyInfo_Patch.cs -token $version$
9 | ) else (
10 | "%aversion%" patch -ver %version% -in %projectdir%\Properties\AssemblyInfo.cs -out %projectdir%\Properties\AssemblyInfo_Patch.cs -token $version$
11 | )
12 |
13 |
--------------------------------------------------------------------------------
/scripts/push.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | set version=%1
4 |
5 | if "%version%"=="" (
6 | echo Please remember to specify which version to push as an argument.
7 | goto exit_fail
8 | )
9 |
10 | set reporoot=%~dp0\..
11 | set destination=%reporoot%\deploy
12 |
13 | if not exist "%destination%" (
14 | echo Could not find %destination%
15 | echo.
16 | echo Did you remember to build the packages before running this script?
17 | )
18 |
19 | set nuget=%reporoot%\tools\NuGet\NuGet.exe
20 |
21 | if not exist "%nuget%" (
22 | echo Could not find NuGet here:
23 | echo.
24 | echo "%nuget%"
25 | echo.
26 | goto exit_fail
27 | )
28 |
29 |
30 | "%nuget%" push "%destination%\*.%version%.nupkg" -Source https://www.nuget.org/api/v2/package
31 | if %ERRORLEVEL% neq 0 (
32 | echo NuGet push failed.
33 | goto exit_fail
34 | )
35 |
36 |
37 |
38 |
39 |
40 |
41 | goto exit_success
42 | :exit_fail
43 | exit /b 1
44 | :exit_success
45 |
--------------------------------------------------------------------------------
/scripts/release.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | set scriptsdir=%~dp0
4 | set root=%scriptsdir%\..
5 | set deploydir=%root%\deploy
6 | set project=%1
7 | set version=%2
8 |
9 | if "%project%"=="" (
10 | echo Please invoke the build script with a project name as its first argument.
11 | echo.
12 | goto exit_fail
13 | )
14 |
15 | if "%version%"=="" (
16 | echo Please invoke the build script with a version as its second argument.
17 | echo.
18 | goto exit_fail
19 | )
20 |
21 | set Version=%version%
22 |
23 | if exist "%deploydir%" (
24 | rd "%deploydir%" /s/q
25 | )
26 |
27 | pushd %root%
28 |
29 | dotnet restore
30 | if %ERRORLEVEL% neq 0 (
31 | popd
32 | goto exit_fail
33 | )
34 |
35 | dotnet pack "%root%/%project%" -c Release -o "%deploydir%" /p:PackageVersion=%version%
36 | if %ERRORLEVEL% neq 0 (
37 | popd
38 | goto exit_fail
39 | )
40 |
41 | call scripts\push.cmd "%version%"
42 |
43 | popd
44 |
45 |
46 |
47 |
48 |
49 |
50 | goto exit_success
51 | :exit_fail
52 | exit /b 1
53 | :exit_success
--------------------------------------------------------------------------------
/tools/NuGet/nuget.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebus-org/Topper/31bb9cbeea8b552cf0f72947d9ae39e325b0a5c6/tools/NuGet/nuget.exe
--------------------------------------------------------------------------------
/tools/aversion/Aversion.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebus-org/Topper/31bb9cbeea8b552cf0f72947d9ae39e325b0a5c6/tools/aversion/Aversion.exe
--------------------------------------------------------------------------------