Legacy Debugging - Arthyon/microservice-poc GitHub Wiki

Depending on which type of service you are debugging, the approach will be a bit different.

In VSCode, the launch.json in this repository is set up correctly, so debugging should work out of the box.

  • Start the environment (docker-compose up)
  • In the Debug-view in VSCode, choose the service you want to debug.
  • If it's a .Net-service, choose the process from the process picker that executes <servicename>.dll

General debugging

In general, some things need to be set up correctly for debugging to work.

  • The debugger must be able to attach to a running service. We do not want to launch a single service using the debugger as it becomes difficult to set up the rest of the environment.
  • The debugger must receive instructions for how the application in the container is mapped to source code on the local computer.

Debugging node

When debugging node, it is important to create source maps to map the generated (generally minified) js to our original typescript-files. Node must also be started in a way to accept debugging.

Expose debug port

All node-services should expose port 9229 out of the container in development and node should be run using the --inspect=0.0.0.0:9229-flag. This instructs node to set up a debugger on port 9229. The port can be changed, but it is important to specify 0.0.0.0. Without that, it is not possible to connect from outside the container (i.e. your local machine). Note: It is also possible to send SIGUSR1 to a running node process to turn on debugging.

In this PoC, the inspect-flag is set in pm2.json as node_args, leveraging pm2-functionality to set up debugging internally.

In our docker.compose.override.yml-file, we should map the container port 9229 to a port on our local machine. Each node-service should be mapped to a unique port. This port is the port specified in launch.json and is the one in use when VSCode is connecting to the service, NOT the container port 9229.

Note: In the PoC, service1 has mapped the container port 9229 to the same port on localhost. All additional services should map to something else both in docker-compose.override.yml and in launch.json.

Source maps

In tsconfig.json, we have specified that inline source maps should be generated. It is possible to separate the source maps, but more moving parts means more places stuff can break, so this guide will not cover how node debugging matches sources with external source maps. sourceMaps is set to true in launch.json to force VSCode to load them.

Mapping local file system to container

Using the properties localRoot and remoteRoot in launch.json, we set up the relationship between code inside and outside the container. remoteRoot should match the directory specified in the Dockerfile, and localRoot will usually be on the form ${workspaceFolder}/<servicename>.

Debugging .Net

To debug .Net, we have installed vsdbg in the development image. .Net-source code needs to be one directory deeper than node-projects, as we are redirecting build output (using Directory.Build.props) outside of the source directory. We need to do this so our container artifacts does not collide with artifacts from a local build when hot reloading inside the container. The development-image will also generate pdb-files by building with a debug configuration.

Mapping local file system to container

In launch.json we use the property sourceFileMap to map the container file system to the local file system. We want to point to the directory containing our source-code, which will usually be on the form {workspaceFolder}/<servicename>/src.

Attaching to .net-process in container

To attach the debugger, we run the debugger using docker exec -i <containername> /vsdbg/vsdbg. This launches the debugger we installed in the development image, and we are presented with a list of all processes running in the container to attach to. We want to attach to the process running our code, which usually references the servicename in the built dll. We are using docker-compose to set a container name, using the property container_name in docker-compose.yml. Without that setting, the container name would be randomly generated on each startup, and we would have to modify launch.json each time we wanted to debug.

To realize the command above, we use the pipeTransport-property in launch.json.