[HEALTHCHECK] Enhance healthcheck logging

    Add a timestamp to every log entry
    Make a log entry when the application starts
    Make a single log entry (instead of 2) for the outbound HTTP requests to the k8s API
    Update node.js base image to the 16.x.x LTS release (from 14.x.x).

Issue-ID: DCAEGEN2-2958
Issue-ID: DCAEGEN2-2983
Signed-off-by: Jack Lucas <jflos@sonoris.net>
Change-Id: Ib275afbd0c871d4e82dda269606d243f3b6d89e4
diff --git a/healthcheck-container/Changelog.md b/healthcheck-container/Changelog.md
new file mode 100644
index 0000000..7e8db8d
--- /dev/null
+++ b/healthcheck-container/Changelog.md
@@ -0,0 +1,12 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/)
+and this project adheres to [Semantic Versioning](http://semver.org/).
+
+## [2.3.0] - 2021-11-15
+* [DCAEGEN2-2983] Update Docker base image to node.js 16.x (the latest LTS release).
+* [DCAEGEN2-2958] Make sure all logging is directed to stdout/stderr. Enhance logging:
+   * Add a timestamp to every log entry.
+   * Make a log entry when the application starts.
+   * Make a single log entry (instead of 2) for each outbound k8s API call.
diff --git a/healthcheck-container/Dockerfile b/healthcheck-container/Dockerfile
index 6a546c6..cf93791 100644
--- a/healthcheck-container/Dockerfile
+++ b/healthcheck-container/Dockerfile
@@ -17,7 +17,7 @@
 # limitations under the License.
 # ============LICENSE_END=========================================================
 #
-FROM node:14.17.0-alpine3.13
+FROM node:16.12.0-alpine3.14
 RUN mkdir -p /opt/app \
   && adduser -D -h /opt/app health
 COPY *.js /opt/app/
diff --git a/healthcheck-container/get-status.js b/healthcheck-container/get-status.js
index f3c4f97..1480e21 100644
--- a/healthcheck-container/get-status.js
+++ b/healthcheck-container/get-status.js
@@ -1,6 +1,7 @@
 /*
 Copyright(c) 2018 AT&T Intellectual Property. All rights reserved.
 Copyright(c) 2020 Nokia Intellectual Property. All rights reserved.
+Copyright(c) 2021 J. F. Lucas.  All rights reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -21,6 +22,7 @@
 
 const fs = require('fs');
 const https = require('https');
+const log = require('./log');
 
 const K8S_CREDS = '/var/run/secrets/kubernetes.io/serviceaccount';
 const K8S_HOST = 'kubernetes.default.svc.cluster.local';	// Full name to match cert for TLS
@@ -38,7 +40,7 @@
 
     let ret =
     {
-        type: "summary",
+        type: 'summary',
         count: 0,
         ready: 0,
         items: []
@@ -64,24 +66,25 @@
     // Make GET request to Kubernetes API
     const options = {
         host: K8S_HOST,
-        path: "/" + path,
+        path: '/' + path,
         ca : ca,
         headers: {
             Authorization: 'bearer ' + token
         }
     };
-    console.log ("request url: " + options.host + options.path);
+
     const req = https.get(options, function(resp) {
-        let rawBody = "";
-        resp.on("data", function(data) {
+        let rawBody = '';
+        resp.on('data', function(data) {
             rawBody += data;
         });
-        resp.on("error", function (error) {
-            console.error("error: " + error);
+        resp.on('error', function (error) {
+            log.debug(`k8s API query: ${options.host}${options.path} -- error: ${error}`);
             callback(error, null, null)
         });
-        resp.on("end", function() {
-            console.log ("status: " + resp.statusCode ? resp.statusCode: "NONE")
+        resp.on('end', function() {
+            const deployment = JSON.parse(rawBody);
+            log.debug (`k8s API query: ${options.host}${options.path}  -- status: ${resp.statusCode ? resp.statusCode: 'NONE'}`);
             callback(null, resp, JSON.parse(rawBody));
         });
     });
@@ -92,7 +95,7 @@
     // Expect item to be of the form {namespace: "namespace", deployment: "deployment_name"}
     return new Promise(function(resolve, reject){
         const path = K8S_PATH + item.namespace + '/deployments/' + item.deployment;
-        queryKubernetes(path, function(error, res, body){
+        queryKubernetes(path, function(error, res, body) {
             if (error) {
                 reject(error);
             }
diff --git a/healthcheck-container/healthcheck.js b/healthcheck-container/healthcheck.js
index 574859f..9197e4d 100644
--- a/healthcheck-container/healthcheck.js
+++ b/healthcheck-container/healthcheck.js
@@ -31,9 +31,11 @@
 const UNHEALTHY = 500;
 const UNKNOWN = 503;
 
-const EXPECTED_COMPONENTS='/opt/app/expected-components.json'
+const EXPECTED_COMPONENTS = '/opt/app/expected-components.json'
+const LISTEN_PORT = 8080;
 
 const fs = require('fs');
+const log = require('./log')
 
 // List of deployments expected to be created via Helm
 let helmDeps = [];
@@ -41,8 +43,8 @@
     helmDeps = JSON.parse(fs.readFileSync(EXPECTED_COMPONENTS, {encoding: 'utf8'}));
 }
 catch (error) {
-    console.log(`Could not access ${EXPECTED_COMPONENTS}: ${error}`);
-    console.log ('Using empty list of expected components');
+    log.error(`Could not access ${EXPECTED_COMPONENTS}: ${error}`);
+    log.error ('Using empty list of expected components');
 }
 
 const status = require('./get-status');
@@ -82,10 +84,11 @@
 // Simple HTTP server--any incoming request triggers a health check
 const server = http.createServer(function(req, res) {
     checkHealth(function(ret) {
-        console.log ((new Date()).toISOString() + ": " + JSON.stringify(ret));
+        log.info(`Incoming request: ${req.url} -- response: ${JSON.stringify(ret)}`);
         res.statusCode = ret.status;
         res.setHeader('Content-Type', 'application/json');
         res.end(JSON.stringify(ret.body || {}), 'utf8');
     });
 });
-server.listen(8080);
+server.listen(LISTEN_PORT);
+log.info(`Listening on port ${LISTEN_PORT} -- expected components: ${JSON.stringify(helmDeps)}`);
diff --git a/healthcheck-container/log.js b/healthcheck-container/log.js
new file mode 100644
index 0000000..655ac6b
--- /dev/null
+++ b/healthcheck-container/log.js
@@ -0,0 +1,26 @@
+/*
+Copyright(c) 2021 J. F. Lucas.  All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and limitations under the License.
+*/
+exports.info = (message) => {
+    console.log(`${(new Date()).toISOString()}: ${message}`);
+}
+
+exports.error = (message) => {
+    console.error(`${(new Date()).toISOString()}: ${message}`);
+}
+
+exports.debug = (message) => {
+    console.debug(`${(new Date()).toISOString()}: ${message}`);
+}
diff --git a/healthcheck-container/package.json b/healthcheck-container/package.json
index 6b91448..e0dcd25 100644
--- a/healthcheck-container/package.json
+++ b/healthcheck-container/package.json
@@ -1,7 +1,7 @@
 {
   "name": "k8s-healthcheck",
   "description": "DCAE healthcheck server",
-  "version": "2.0.0",
+  "version": "2.3.0",
   "main": "healthcheck.js",
   "author": "author",
   "license": "(Apache-2.0)"
diff --git a/healthcheck-container/pom.xml b/healthcheck-container/pom.xml
index b4fab76..f15a6ea 100644
--- a/healthcheck-container/pom.xml
+++ b/healthcheck-container/pom.xml
@@ -29,7 +29,7 @@
   <groupId>org.onap.dcaegen2.deployments</groupId>
   <artifactId>healthcheck-container</artifactId>
   <name>dcaegen2-deployments-healthcheck-container</name>
-  <version>2.2.0</version>
+  <version>2.3.0</version>
   <url>http://maven.apache.org</url>
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>