Arduino script stops after random period of time

First thing I notice is that there is too much comments in the code to be readable ...

example this line of code is perfectly self documenting. No need to add any comment.
int runningPin = 2; // I'm using digital pin 2.

It means less typing for you , less reading for the debuggers. Comments should tell the why (and sometimes the what) but never the how.

Removed lots of unneeded comments -> found 2 places with questions

#include <Wire.h>
#include <HMC5883L.h>
#include <ADXL345.h>
#include <SD.h>

HMC5883L compass;
ADXL345 accel;

int runningPin = 2; 
int failurePin = 3;
int areConnected = 0;
int recordCount = 0;

const int chipSelect = 10;
File dataFile;

void setup()
{
  Serial.begin(9600); 
  Wire.begin(); 
  
  pinMode(runningPin, OUTPUT); 
  pinMode(failurePin, OUTPUT);

  compass = HMC5883L(); 
  accel = ADXL345(); 
  
  compass.EnsureConnected();
  accel.EnsureConnected();
  if (compass.IsConnected && accel.IsConnected)
  {
    areConnected = true;
    Serial.println("Connected to HMC5883L and ADXL345.");
    digitalWrite(runningPin, HIGH);
    digitalWrite(failurePin, LOW);
  }
  else
  {
    areConnected = false;
    digitalWrite(runningPin, LOW);
    digitalWrite(failurePin, HIGH);    
    
    if (compass.IsConnected == 0)
      Serial.println("Could not connect to HMC5883L.");
    if (accel.IsConnected == 0)
      Serial.println("Could not connect to ADXL345.");
  }


  if (areConnected)
  { 
    compass.SetScale(1.3); 
    compass.SetMeasurementMode(Measurement_Continuous); 

    accel.SetRange(2, true); // Set the range of the accelerometer to a maximum of 2G.
    accel.EnableMeasurements(); 
  }
  Serial.print("Initializing SD card...");
  // **SD** make sure that the default chip select pin is set to output, even if you don't use it:
  pinMode(SS, OUTPUT);
  
  // **SD** see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    digitalWrite(runningPin, LOW);
    digitalWrite(failurePin, HIGH);
    while (1);
  }
  
  Serial.println("card initialized.");
  
  dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (! dataFile) {
    Serial.println("error opening datalog.txt");
    digitalWrite(runningPin, LOW);
    digitalWrite(failurePin, HIGH);
    while (1) ;
  }
  digitalWrite(runningPin, HIGH);
}

void loop()
{
  if (areConnected)
  {
    MagnetometerScaled magnetometerReadings = compass.ReadScaledAxis();
    AccelerometerScaled accelerometerReadings = accel.ReadScaledAxis();
    
    float headingNTC = CalculateHeadingNotTiltCompensated(magnetometerReadings);
    float headingTC = CalculateHeadingTiltCompensated(magnetometerReadings, accelerometerReadings);
    
    // Output the data via the serial port.
    Output(headingNTC, headingTC);
    
    dataFile.flush();
    delay(250); 
  }
}

float CalculateHeadingTiltCompensated(MagnetometerScaled mag, AccelerometerScaled acc)
{
  // We are swapping the accelerometers axis as they are opposite to the compass the way we have them mounted.
  // We are swapping the signs axis as they are opposite.
  float accX = -acc.XAxis;
  float accY = -acc.YAxis;
  
  float rollRadians = asin(accY);
  float pitchRadians = asin(accX);
  
  // We cannot correct for tilt over 40 degrees with this algorithm, if the board is tilted as such, return 0.
  if (rollRadians > 0.78 || rollRadians < -0.78 || pitchRadians > 0.78 || pitchRadians < -0.78)
  {
    return 0;  ?? HOW TO DIFFERENTIATE BETWEEN A VALID 0 AND AN ERRONEOUS ZERO
  }
  
  // precompute vars.
  float cosRoll = cos(rollRadians);
  float sinRoll = sin(rollRadians);  
  float cosPitch = cos(pitchRadians);
  float sinPitch = sin(pitchRadians);
  
  // tilt compensation algorithm.
  float Xh = mag.XAxis * cosPitch + mag.ZAxis * sinPitch;
  float Yh = mag.XAxis * sinRoll * sinPitch + mag.YAxis * cosRoll - mag.ZAxis * sinRoll * cosPitch;
  
  float heading = atan2(Yh, Xh);
    
  return heading;
}

float CalculateHeadingNotTiltCompensated(MagnetometerScaled mag)
{
   // Calculate heading when the magnetometer is level, then correct for signs of axis.
   ?? WHERE IS THIS SIGN CORRECTION DONE???
   float heading = atan2(mag.YAxis, mag.XAxis);
   return heading;
}

float RadiansToDegrees (float rads)
{
  // Degrees should be between 0..360
  while (rads < 0) rads += 2*PI;
  while (rads > 2*PI) rads -= 2*PI;

  float heading = rads * 180/PI;
  return heading;
}

void Output(float headingNTC, float headingTC)
{
  recordCount++;  // YOU SHOULD UPDATE recordCount TO HAVE MEANINGFUL LOGGING

  /*
  if (recordCount <= 50) {
    recordCount ++;
    digitalWrite(runningPin, HIGH);
  }  else {
    recordCount ++;
    digitalWrite(runningPin, LOW);
  }
  */
  
  // precompute
  float HNTC = RadiansToDegrees(headingNTC)
  float HTC = RadiansToDegrees(headingTC)  

  if (SD.exists("datalog.txt"))  // MAKE A RETRY MECHANISM HERE
  {
    dataFile.print(HNTC);
    dataFile.print(", ");   
    dataFile.println(HTC);  
 
    Serial.print(recordCount);
    Serial.print(", ");
    Serial.print("Heading: ");
    Serial.print(HNTC);
    Serial.print(" Heading (Tilt Compensated): ");   
    Serial.println(HTC);

  } else {

    Serial.print("Error recording at ");
    Serial.println(recordCount);
    digitalWrite(runningPin, LOW);
    digitalWrite(failurePin, HIGH);
    // while(1); // <<<<<<<<<<<<<<<<<<<<<<<<<< I would remove this one so it can retry if the disk has one bad read.
  }
}

your code has 3 places where it has a blocking stop. E.g. if the SD card does make one bad read (Exists), it blocks.
You could rewrite it to have at least 3-5 retries there (log the number of retries! as it can indicate a bad disk.

my 2 cents