Ćwiczenie — monitorowanie błędów przejściowych w aplikacji przy użyciu rejestrowania

Ukończone

W tym ćwiczeniu ulepszymy bieżące rejestrowanie aplikacji do czatu, tworząc kod z poprzednich ćwiczeń.

Dla uproszczenia koncentrujemy się na rejestrowaniu komunikatów w konsoli w tym ćwiczeniu. Poniższa lista zawiera informacje, które chcesz rejestrować:

  • Czas wystąpienia błędu
  • Liczba ponownych prób
  • Czas naprawienia błędu
  • Lokalizacja w kodzie
  • Błędy przejściowe powinny znajdować się na poziomie ostrzeżeń
  • Wyjątki powodujące zamknięcie aplikacji powinny być błędami

Napiszesz kod w taki sposób, aby można było go rozbudować w przyszłości w celu zapisywania komunikatów dziennika do plików lub innych usług, takich jak Azure Application Insights.

Dodawanie dostawcy rejestrowania

Firma Microsoft oferuje pakiet NuGet Microsoft.Extensions.Logging, który dodaje zestaw doskonałych funkcji rejestrowania. Można go dostosować z użyciem dostawców. Pakiet ma wbudowanego dostawcę konsoli, który pozwala na rejestrowanie komunikatów w konsoli. Możesz również użyć nowych funkcji wstrzykiwania zależności platformy .NET Core, aby umożliwić wyświetlanie komunikatów dzienników w celu wydrukowania klasy, w której się znajdują.

  1. Upewnij się, że nadal jesteś w folderze chatapp-retry języka C# za pomocą następującego polecenia.

    cd ~/mslearn-handle-transient-errors-in-your-app/csharp/chatapp-retry/
    
  2. W usłudze Cloud Shell dodaj Microsoft.Extensions.Logging do aplikacji, instalując następujące trzy zależności.

    dotnet add package Microsoft.Extensions.Logging --version 2.2.0
    
    dotnet add package Microsoft.Extensions.DependencyInjection --version 2.2.0
    
    dotnet add package Microsoft.Extensions.Logging.Console --version 2.2.0
    

    Przed kontynuowaniem tego ćwiczenia upewnij się, że pomyślnie zainstalowano wszystkie trzy pakiety.

  3. Wprowadź poniższe polecenie w edytorze usługi Cloud Shell.

    code .
    

    Zostanie otwarty edytor kodu, a wszystkie pliki w bieżącym folderze będą wyświetlane w oknie nawigacji po lewej stronie.

  4. Wybierz plik Program.cs w oknie nawigacji po lewej stronie, aby otworzyć go w edytorze.

  5. Odwołaj się do elementu Microsoft.Extensions.Logging w pliku Program.cs, dodając instrukcje using, jak pokazano w poniższym fragmencie kodu.

    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.DependencyInjection;
    
  6. Dodaj prywatny element członkowski do klasy dla naszego obiektu rejestrowania.

    private static ILogger main_logger;
    

Dodawanie rejestrowania

  1. Dodaj następujący kod do początku metody Main() w pliku Program.cs.

    // Instantiate Dependency Injection and configure logger
    var serviceProvider = new ServiceCollection()
        .AddLogging(cfg => cfg.AddConsole())
        .Configure<LoggerFilterOptions>(cfg => cfg.MinLevel=LogLevel.Debug)
        .BuildServiceProvider();
    
    // Set instances of logger
    var logger = serviceProvider.GetService<ILogger<RetryPolicy>>();
    main_logger = serviceProvider.GetService<ILogger<Program>>();
    
    retries = new RetryPolicy(logger);
    

    Powyższy kod tworzy wystąpienie klasy ServiceCollection, dodaje dostawcę konsoli i ustawia domyślny poziom rejestrowania w celu debugowania. W następnych dwóch wierszach zostaną utworzone dwa rejestratory: jeden do użycia w klasie zasad ponawiania, a drugi dla głównej aplikacji.

  2. Utworzyliśmy zasady ponawiania w poprzednim kodzie, więc teraz należy zmienić następujący kod:

        private static RetryPolicy retries = new RetryPolicy();
    

    Do:

        private static RetryPolicy retries;
    
  3. Zastąp element getAllChats następującą aktualizacją. Ten kod zastępuje wszystkie Console.WriteLine wywołania związane z błędami systemowymi, ale utrzymuje wywołania Console.WriteLine interfejsu użytkownika.

    private static void getAllChats()
    {
        messages = database.GetCollection<ChatMessage>(collectionName);
        try
        {
            allMessages = messages.Find(new BsonDocument()).ToList();
            foreach (ChatMessage chat in allMessages)
            {
                Console.WriteLine($"{chat.Name}: {chat.Message}");
                main_logger.LogInformation($"{DateTime.Now} - Messages read from database.");
            }
            Console.WriteLine("\n");
        }
        catch (MongoDB.Driver.MongoConnectionException e)
        {
            if (retries.CanRetry())
            {
                diagnose(e);
                getAllChats(); //retry
            } else {
                main_logger.LogError($"{DateTime.Now} - Maximum retries - need to close.");
                throw e;
            }
        }
        catch (System.TimeoutException e)
        {
            if (retries.CanRetry())
            {
                diagnose(e);
                getAllChats(); //retry
            } else {
                main_logger.LogError($"{DateTime.Now} - Maximum retries - need to close.");
                throw e;
            }
        }
        catch (Exception e)
        {
            main_logger.LogError($"{DateTime.Now} - Full stack trace: {e.StackTrace}");
            throw e;
        }
    }
    

    Rozszerzyliśmy rejestrowanie z głównej aplikacji, a teraz skupimy się utworzonej wcześniej klasie RetryPolicy. Klasa RetryPolicy musi również przeprowadzić rejestrowanie, aby dodać istotne informacje na temat błędów przejściowych.

  4. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Zapisz.

  5. Wybierz plik RetryPolicy.cs w oknie nawigacji po lewej stronie, aby otworzyć plik RetryPolicy.cs w edytorze kodu.

  6. Dodaj odwołanie do biblioteki rejestrowania za pomocą następującej instrukcji using.

    using Microsoft.Extensions.Logging;
    
  7. Dodaj prywatny element członkowski do klasy dla naszego obiektu rejestrowania.

    private readonly ILogger retrylogger;
    
  8. Zaktualizuj konstruktor klasy przy użyciu następującego kodu, aby utworzyć wystąpienie rejestratora. Zwróć uwagę, że konstruktor przyjmuje teraz parametr typu ILogger.

    public RetryPolicy(ILogger logger)
    {
        retrylogger = logger;
        ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
        configurationBuilder.SetBasePath(Directory.GetCurrentDirectory());
        configurationBuilder.AddJsonFile("appsettings.json");
        configuration = configurationBuilder.Build();
    }
    
  9. Zastąp wywołanie Console.WriteLine w elemencie CanRetry:

    Console.WriteLine($"Retrying: {currentTries}");
    

    Tym:

    retrylogger.LogWarning($"{DateTime.Now} - Retries: {currentTries}");
    
  10. Zapisz plik i zamknij edytor. Użyj poleceń w menu ... w prawym górnym rogu edytora lub użyj klawiszy skrótu Ctrl+S, aby zapisać plik, i Ctrl+Q, aby zamknąć edytor.

  11. Skompiluj i uruchom aplikację.

    dotnet build
    dotnet run
    

Dodawanie dostawcy rejestrowania

Środowisko uruchomieniowe języka Java oferuje bibliotekę import java.util.logging, która dodaje zestaw doskonałych funkcji rejestrowania. Można ją dostosować z użyciem dostawców, komunikaty mogą mieć zastosowane formaty, a biblioteka będzie domyślnie rejestrować w konsoli. Nie musisz dodawać tej biblioteki do aplikacji do czatów, ponieważ jest już ona używana do ukrywania komunikatów dziennika bazy danych MongoDB. Dodasz nowy rejestrator do użycia w głównej aplikacji i zasady ponawiania prób, a następnie dodaj nową bibliotekę czasu, aby zezwolić na dostęp do bieżącej godziny.

  1. W usłudze Cloud Shell przejdź do folderu chatapp-retry węzła.

    cd ~/mslearn-handle-transient-errors-in-your-app/java/chatapp-retry/
    
  2. Edytuj plik javaChat.java w edytorze kodu.

    code javaChat.java
    
  3. Zaimportuj biblioteki procesów pomocniczych czasu przez dodanie poniższych instrukcji import do pliku javaChat.java.

    import java.time.format.DateTimeFormatter;
    import java.time.LocalDateTime;
    
  4. Dodaj do niej zmienną javaChat prywatną składową rejestratora, która może być używana w całej aplikacji.

    private static Logger logger = Logger.getLogger( javaChat.class.getName() );
    
  5. Zaktualizuj element printAllMessages do następującego kodu. Aktualizacja zastępuje wywołania System.out.Println w blokach catch. Nadal istnieją wywołania elementu System.out.Println dla interfejsu użytkownika.

    private static void printAllMessages (MongoCollection<Document> collection) throws InterruptedException {
        try {
            // Return all messages
            collection.find().forEach((Consumer<Document>) document -> {
                System.out.printf("%s: %s\n", document.get("name"), document.get("message"));
            });
        }
        catch (com.mongodb.MongoCommandException e) {
            if (retries.canRetry())
            {
                diagnose(e);
                printAllMessages(collection); //retry
            } else {
                System.out.println("Maximum retries - need to close.");
                throw e;
            }
        }
        catch (com.mongodb.MongoSecurityException e) {
            if (retries.canRetry())
            {
                diagnose(e);
                printAllMessages(collection); //retry
            } else {
                System.out.println("Maximum retries - need to close.");
                throw e;
            }
        }
        catch (Exception e) {
            diagnose(e);
            throw e;
        }
    }
    
  6. Klasa zasad ponawiania musi również przeprowadzić rejestrowanie, aby dodać istotne informacje na temat błędów przejściowych.

  7. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Zapisz.

  8. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Otwórz, w oknie dialogowym wpisz RetryPolicy.java i naciśnij klawisz Enter.

  9. Zaimportuj biblioteki rejestrowania i czasu przez dodanie poniższych instrukcji import do klasy.

    import java.util.logging.Logger;
    import java.util.logging.Level;
    import java.time.format.DateTimeFormatter;
    import java.time.LocalDateTime;
    
  10. Zadeklaruj zmienną dla wystąpienia naszego rejestratora.

    private Logger retryLogger = Logger.getLogger( RetryPolicy.class.getName() );
    
  11. Zastąp wywołanie System.out.printf w elemencie canRetry() komunikatem rejestrowania:

    System.out.printf("Retrying: %s\n", currentTries);
    

    Tym:

    retryLogger.log(Level.WARNING, LocalDateTime.now() + " - Retrying: " + currentTries);
    
  12. Zapisz plik i zamknij edytor. Użyj poleceń w menu ... w prawym górnym rogu edytora lub użyj klawiszy skrótu Ctrl+S, aby zapisać plik, i Ctrl+Q, aby zamknąć edytor.

  13. Skompiluj i uruchom aplikację.

    javac -cp .:lib/* -d . javaChat.java RetryPolicy.java
    java -cp .:lib/* learn.javachatapp.javaChat
    

Dodawanie dostawcy rejestrowania

Ze względu na to, że środowisko Node.js opiera się na języku JavaScript, wbudowane biblioteki rejestrowania nie są udostępniane. Istnieje wiele innych bibliotek rejestrowania, ale Twój zespół zdecydował się na standaryzację w systemie Winston we wszystkich aplikacjach węzłów. Pakiet Winston może domyślnie zapisywać w konsoli i oferuje również opcje różnych operacji transportu i formatów komunikatów dziennika.

  1. Upewnij się, że nadal znajdujesz się w folderze Node.js chatapp-retry , uruchamiając następujące polecenie.

    cd ~/mslearn-handle-transient-errors-in-your-app/node/chatapp-retry/
    
  2. Zainstaluj pakiet Winston.

    npm install winston
    
  3. Edytuj plik server.js w edytorze kodu.

    code server.js
    
  4. Wymagaj nowej biblioteki rejestrowania Winston, dodając język JavaScript do wiersza 16.

    const { createLogger, format, transports } = require('winston');
    
    const logger = createLogger({
      level: 'debug',
      format: format.combine(
        format.colorize(),
        format.timestamp({
          format: 'YYYY-MM-DD HH:mm:ss'
        }),
        format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
      ),
      transports: [new transports.Console()]
    });
    
  5. Zastąp wszystkie wywołania console.log nowym rejestratorem.

    // Connect to MongoDB
    function connectWithRetry() {
      mongoose.connect( dbUrl, options )
      .then(() => logger.log('info',  'Connection to MongoDB successful') )
      .catch(function(e) {
        if (retries.checkRetries()) {
          connectWithRetry();
        } else {
          logger.log('error',  'Error occured trying to connect to the database.');
        }
      });
    }
    
    // Get all messages from the database
    app.get('/messages', (req, res) => {
      Message.find({})
        .then(messages => {
          res.send(messages);
          logger.log('info', 'Messages Refreshed.');
        })
        .catch(function(e) {
          logger.log('error', 'Error reading from the database.' + e);
        });
      }
    );
    
    // Save a message
    app.post('/messages', (req, res) => {
      var message = new Message(req.body);
      message.save()
        .then( () => {
          res.sendStatus(200);
          logger.log('info', 'Message Posted.');
        })
        .catch(function(e) {
          logger.log('error', 'Error saving to the database.' + e);
        });
      }
    );
    
    // Delete all messages from the database
    app.post('/deleteall', (req, res) => {
      Message.deleteMany({})
        .then( () => {
          res.sendStatus(200);
          logger.log('info', 'Messages Deleted.');
        })
        .catch(function(e) {
          logger.log('error', 'Error deleting from database.' + e);
        });
      }
    );
    
  6. Klasa zasad ponawiania musi również przeprowadzić rejestrowanie, aby dodać istotne informacje na temat błędów przejściowych.

  7. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Zapisz.

  8. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Otwórz, w oknie dialogowym wpisz retryPolicy.js i naciśnij klawisz Enter.

  9. Rozszerz plik retryPolicy.js, aby rejestrować informacje o błędach przejściowych, dodając kod umożliwiający użycie pakietu Winston w wierszu 4.

    const { createLogger, format, transports } = require('winston');
    
    const logger = createLogger({
      level: 'debug',
      format: format.combine(
        format.colorize(),
        format.timestamp({
          format: 'YYYY-MM-DD HH:mm:ss'
        }),
        format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
      ),
      transports: [new transports.Console()]
    });
    
  10. Zastąp wiersz console.log w metodzie checkRetries:

    console.log('Retrying: ' + this._currentTries);
    

    Tym:

    logger.log('warn', 'Retrying: ' + this._currentTries);
    
  11. Zapisz plik i zamknij edytor. Użyj poleceń w menu ... w prawym górnym rogu edytora lub użyj klawiszy skrótu Ctrl+S, aby zapisać plik, i Ctrl+Q, aby zamknąć edytor.

  12. Uruchom aplikację.

    npm start
    

Napiwek

Jeśli w aplikacji nie masz już otwartej strony przeglądarki, uruchom polecenie curl -X POST http://localhost:8888/openPort/8000; i kliknij adres URL.

Testowanie nowego rejestrowania

  1. Jeśli zapora jest nadal włączona dla usługi Azure Cosmos DB, nie można połączyć aplikacji do czatów z bazą danych. W przeciwnym razie jeśli aplikacja do czatów jest nadal uruchomiona, wykonaj następujące kroki, aby włączyć zaporę.

  2. Zaloguj się do witryny Azure Portal dla piaskownicy.

  3. W menu witryny Azure Portal lub na stronie głównej wybierz pozycję Azure Cosmos DB.

  4. Powinno zostać wyświetlone konto bazy danych z nazwą rozpoczynającą się od learn-cosmos-db-. Wybierz to konto bazy danych.

  5. W panelu usługi Azure Cosmos DB wybierz pozycję Zapory i sieci wirtualne.

  6. Wybierz pozycję Wybrane sieci.

  7. Usuń zaznaczenie pola wyboru Zezwalaj na dostęp z witryny Azure Portal.

  8. Zaznacz pole wyboru Rozumiem, że bieżące ustawienia spowodują zablokowanie wszystkich sieci wirtualnych i adresów IP, w tym witrynę Azure Portal.

  9. Wybierz pozycję Zapisz.

  10. Wróć do usługi Cloud Shell i spróbuj odświeżyć komunikaty. W konsoli powinny zostać wyświetlone nowe komunikaty rejestrowania, podobne do poniższych.

    warn: csharp_chatapp_retry.RetryPolicy[0]
          20/03/2019 10:37:24 - Retries: 1
    warn: csharp_chatapp_retry.Program[0]
          Exception raised: System.TimeoutException

    warn: csharp_chatapp_retry.RetryPolicy[0]
          20/03/2019 10:37:29 - Retries: 2
    warn: csharp_chatapp_retry.Program[0]
          Exception raised: System.TimeoutException

    warn: csharp_chatapp_retry.RetryPolicy[0]
          20/03/2019 10:38:34 - Retries: 3
    warn: csharp_chatapp_retry.Program[0]
          Exception raised: System.TimeoutException

    warn: csharp_chatapp_retry.RetryPolicy[0]
          20/03/2019 10:39:39 - Retries: 4
    warn: csharp_chatapp_retry.Program[0]
          Exception raised: System.TimeoutException

    warn: csharp_chatapp_retry.RetryPolicy[0]
          20/03/2019 10:40:44 - Retries: 5
    fail: csharp_chatapp_retry.Program[0]
          20/03/2019 10:41:44 - Maximum retries - need to close.
Mar 20, 2019 11:35:30 AM learn.javachatapp.RetryPolicy checkRetries
WARNING: 2019-03-20T11:35:30.484755400 - Retrying: 1
Mar 20, 2019 11:35:30 AM learn.javachatapp.javaChat diagnose
WARNING: 2019-03-20T11:35:30.523755300 - Exception raised: com.mongodb.MongoSecurityException: Exception authenticating
Mar 20, 2019 11:35:30 AM learn.javachatapp.RetryPolicy checkRetries
WARNING: 2019-03-20T11:35:30.680967900 - Retrying: 2
Mar 20, 2019 11:36:30 AM learn.javachatapp.javaChat diagnose
WARNING: 2019-03-20T11:36:30.682929400 - Exception raised: com.mongodb.MongoSecurityException: Exception authenticating
Mar 20, 2019 11:36:30 AM learn.javachatapp.RetryPolicy checkRetries
WARNING: 2019-03-20T11:36:30.898297400 - Retrying: 3
Mar 20, 2019 11:37:30 AM learn.javachatapp.javaChat diagnose
WARNING: 2019-03-20T11:37:30.901201300 - Exception raised: com.mongodb.MongoSecurityException: Exception authenticating
Mar 20, 2019 11:37:31 AM learn.javachatapp.RetryPolicy checkRetries
WARNING: 2019-03-20T11:37:31.125043 - Retrying: 4
Mar 20, 2019 11:38:31 AM learn.javachatapp.javaChat diagnose
WARNING: 2019-03-20T11:38:31.126188100 - Exception raised: com.mongodb.MongoSecurityException: Exception authenticating
Mar 20, 2019 11:38:31 AM learn.javachatapp.RetryPolicy checkRetries
WARNING: 2019-03-20T11:38:31.329202 - Retrying: 5
Mar 20, 2019 11:39:31 AM learn.javachatapp.javaChat printAllMessages
SEVERE: 2019-03-20T11:39:31.330523900 - Maximum retries - need to close.
Exception in thread "main" com.mongodb.MongoSecurityException: Exception authenticating
2019-03-20 14:06:45 warn: Retrying: 1
2019-03-20 14:06:45 warn: Retrying: 2
2019-03-20 14:07:45 warn: Retrying: 3
2019-03-20 14:08:45 warn: Retrying: 4
2019-03-20 14:09:45 warn: Retrying: 5
2019-03-20 14:10:45 error: Error occurred trying to connect to the database.