Laden...

Wieso stoppt Windows Dienst nach erster Ausführung?

Erstellt von CoderboyPB vor 3 Jahren Letzter Beitrag vor 3 Jahren 588 Views
C
CoderboyPB Themenstarter:in
327 Beiträge seit 2008
vor 3 Jahren
Wieso stoppt Windows Dienst nach erster Ausführung?

Hallo,
die statische Funktion Run() der Klasse Bot wird nur einmal ausgeführt, dann stoppt der Dienst automatisch.
Was mache ich falsch?


public class Program
    {
        public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning)
                .Enrich.FromLogContext()
                .WriteTo.File(@"C:\SosaBot\LogFile.txt")
                .CreateLogger();

            try
            {
                Log.Information("Starting up the Service");
                CreateHostBuilder(args).Build().Run();
                return;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "-ther was a problem, starting the service");
                return;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            return Host.CreateDefaultBuilder(args)
                .UseWindowsService()
                .ConfigureServices((hostContext, services) =>
                {
                services.AddHostedService<Worker>();
                })
                .UseSerilog();
        }
    }


public class Worker : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                await Bot.Run();
                await Task.Delay(1000 * 60 * 3, stoppingToken);
            }
        }
    }


public static class Bot
    {
        public static async Task Run()
        {
            Log.Information("Starting Bot");

            var userClient = new TwitterClient(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET);

            (await userClient.Search.SearchTweetsAsync("Sportsfreund Sosa"))
                .ToList()
                .ForEach(async tweet =>
                {
                    Log.Information($"retweeting: {tweet.FullText}");
                    await userClient.Tweets.PublishRetweetAsync(tweet.Id);
                });

            Log.Information("Stopping Bot");
        }
    }

PS: Die Zugangsdaten sind da, habe sie aber hier heraus genommen.

16.806 Beiträge seit 2008
vor 3 Jahren

Den einzigen Fehler, den ich so sehe ist, dass Dein Logging kein Sinn macht.

  • Du registierst nen statischen Logger
  • Du registrierst nochmal Serilog

Normalerweise registriert man Serilog so:


        public static int Main(string[] args)
            => RunHostBuilder<Startup>(args);

        public static int RunHostBuilder<TStartup>(string[] args) where TStartup : class
        {
            try
            {
                using IHost host = CreateHostBuilder<TStartup>(args).Build();
                host.Run();

                return 0;
            }
            catch (Exception ex)
            {
                // Log.Logger will likely be internal type "Serilog.Core.Pipeline.SilentLogger"
                // This will log into Console, when app fails on startup
                if (Log.Logger == null || Log.Logger.GetType().Name == "SilentLogger")
                {
                    Log.Logger = new LoggerConfiguration()
                        .MinimumLevel.Debug()
                        .WriteTo.Console()
                        .CreateLogger();
                }

                Log.Fatal(ex, "Host terminated unexpectedly");

                return 1; /* 0 = ok, 1 = general error */
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IHostBuilder CreateHostBuilder<TStartup>(string[] args)
            where TStartup : class
            => Host.CreateDefaultBuilder(args)
                .UseSerilog()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<TStartup>()
                        .CaptureStartupErrors(true);
                });


Oder eben noch

                .UseSerilog((hostingContext, loggerConfiguration) =>
                {
                    loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration);
                })

Wenn Du Serilog über die appsettings.json verwalten willst.

Ansonsten solltest Du ja über Debugging herausfinden, warum der "Worker" (jetzt weiß ich auch, wieso Du im anderen Thread von Worker sprichst; dass Du damit nen BackgroundService meinst haste nicht verraten) sich und damit den Windows Service beendest.
Dazu musst Du nur die Start / Stop etc Methoden entsprechend implementieren und kannst dann rein steppen.

Damit relativiert sich übrigens auch meine Antwort im Azure Thread.
Du musst bei Fragen unbedingt den potentiellen Helfern mitteilen, von was Du sprichst.....

Lösung siehe hier.

C
CoderboyPB Themenstarter:in
327 Beiträge seit 2008
vor 3 Jahren
  1. zu dem Service:

Die Twitter Libary hatte ne Exception geworfen, die aber dummerweise nicht protokolliert wurde ...
Hab das Ganze entdeckt, als ich das noch mal als Standalone Applikation habe laufen lassen.

Die Exception wurde ausgelöst, dadurch, dass Retweets geretweeted wurden.
Fange die Exception jetzt ab und verlasse dann die Schleife direkt. Vorher habe ich die Tweets natürich chronologisch geordnet:


public static async Task Run()
        {
            var userClient = new TwitterClient(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET);

            var tweets = (await userClient.Search.SearchTweetsAsync("Sportsfreund Sosa"))
                 .OrderByDescending(tweet => tweet.CreatedAt)
                 .ToList();

            foreach (var tweet in tweets)
            {
                try
                {
                    await userClient.Tweets.PublishRetweetAsync(tweet.Id);
                    Log.Information($"Retweeted: {{ {tweet.FullText} }}");
                }
                catch (TwitterException)
                {
                    return;
                }
            }
        }

aber das funktioniert jetzt 😃

Was mich halt ärgert ist, dass die Exception nicht geloggt wurde, hätte mir einiges an Arbeit erspart ...

Zum Thema Serilog:
Das habe ich von einem Youtube Video von Tim Corey, ich selber kenne mich mit Serilog nicht aus, aber es scheint sehr mächtig zu sein, so dass ich es mir mal anschauen werde.

C
CoderboyPB Themenstarter:in
327 Beiträge seit 2008
vor 3 Jahren

Nachtrag:
Der statische Logger I ST Serilog, mit UseSerilog wird dann das Programm angewiesen, Serilog zu nutzen.
So in etwa hat Tim Corey es in seinem Video erklärt.

16.806 Beiträge seit 2008
vor 3 Jahren

Der statische Logger I ST Serilog, mit UseSerilog wird dann das Programm angewiesen, Serilog zu nutzen.

Nein, UseSerilog ist ein Shortcut für die Service Konfiguration und injiziert dabei die Factory.
Einfach mal in den Quellcode schauen.

Die Twitter Libary hatte ne Exception geworfen, die aber dummerweise nicht protokolliert wurde ...

Setz den Logger korrekt, dann wird sie protokolliert.
Aber wenn Du meinst, dass meine Aussage falsch ist, dann lass es eben so - musst Du wissen 😃

C
55 Beiträge seit 2020
vor 3 Jahren

Ich nutze Dependency Injection um meinen Logger zu bekommen. Dafür nutze ich das Interface ILogger<T>, was eigentlich sogar der standard Weg ist bzw sein sollte. Den kann ich sogar mit NLog konfigurieren. Ob der auch mit Serilog konfigurierbar ist weiß ich nicht.


private readonly ILogger<MqttSubcriptionWorker> logger;

public MqttSubcriptionWorker(ILogger<MqttSubcriptionWorker> logger)
{
    this.logger = logger;
}

C
CoderboyPB Themenstarter:in
327 Beiträge seit 2008
vor 3 Jahren

Das ist auch der mir bekannte Weg.
Aber wie gesagt, hier zählte einfach nur das Ergebnis.

Wen es interessiert hier das Video. Ab ca. min 20:15
https://www.youtube.com/watch?v=PzrTiz_NRKA

16.806 Beiträge seit 2008
vor 3 Jahren

Ob der auch mit Serilog konfigurierbar ist weiß ich nicht.

Serilog ist ein Sink-Provider, der auch das ILogger-Interface verwendet.
Durch das UseSerilog wird die Factory ausgetauscht, sodass das Logging-Interface selbst das gleiche bleibt aber die Verarbeitungs-Pipeline auf Serilog verändert wird.
Dadurch bleibt aus Entwicker-sicht alles identisch: nur das Ziel wird getauscht.

Die Factory hat aber nichts mit Logger.Log zutun.