What is scripting?

Like all programming, scripting is a way of providing instructions to a computer so you can tell it what to do and when to do it. Programs can be designed to be interacted with manually by a user (by clicking buttons in the GUI or entering commands via the command prompt) or programmatically using other programs (or a mixture of both). 

Consider this web page displayed on your browser – the browser is a program that you can interact with manually, and that program also reads other code (the HTML and CSS that describes the page) to determine what to display.

What is the difference between scripting and coding?

So what exactly is the difference between writing a script and writing code? The answer is simple: scripting is just a particular type of coding. You can think of coding – or programming – as the collective term for providing instructions to a computer. 

These instructions can achieve a whole range of things, from building web pages and writing apps to automating IoT devices, designing databases, or developing a new operating system.

One of the considerations when you start writing these instructions is deciding which programming language to use. There are hundreds of computer languages in existence, with more being developed all the time. 

Different languages are better suited to different use cases, whether that’s manipulating data, generating graphics, or creating tools to help developers write code. Scripting languages are a subset of these programming languages, and different scripting languages are helpful for various tasks.

One of the critical features of scripts is that they provide instructions that are read and executed by another program while it is running. In technical terms, instructions from a script are interpreted at runtime (i.e., when the code is used). 

By contrast, the code is compiled in advance of being run in other programming languages. You can think of this as packaging up the instructions ready for use. This means you get the same behavior each time you run them, but if you want to make a change, you need to recompile the program into a new package and redeliver it. 

Sometimes that’s acceptable, but in other cases, it’s helpful to modify the instructions without recompiling the code first. Scripts allow you to modify the instructions each time the program runs.

(Note that the distinction between interpreted and compiled languages isn’t completely clear cut, but that’s a topic for another day.)

What are the advantages of scripting?

Because scripts provide instructions to other computer programs while running, they are ideal for creating dynamic experiences. One of the significant use cases for scripting languages is web development, where dynamic and responsive experiences are highly valued.

Imagine a basic webpage, such as an online retailer’s page for a particular prod, consisting of text and images. You could create a static version of that webpage with HTML and CSS. 

However, most online retailers have hundreds, if not thousands, of products and therefore pages. Those pages need to be kept up to date with the latest availability and price information. The retailer usually wants to display other information on each page, such as reminders of other products you’ve viewed and the number of items in your basket.

Creating every possible permutation of each webpage in advance as static HTML and CSS would be very inefficient. Instead, we can combine the static HTML and CSS with scripts that call up the dynamic content each time a user loads the page. Scripts populate the web page with relevant content based on your browsing history and product database.

Scripting language examples

The following scripting languages are widely used in web development to create dynamic user experiences (such as social media feeds, recommendations, and search results) and generate pages from templates (such as news and e-commerce sites). 

As we’ll see, web development frameworks have been created for each. Frameworks make building and maintaining experiences with a particular language easier by providing tools and libraries that simplify everyday tasks (such as authenticating users or making database calls).

JavaScript (JS)

JavaScript is a well-known programming language that’s primarily associated with client-side scripting. Client-side scripts run from your browser when you view a web page rather than on the server hosting the website.

JavaScript is used on most modern websites, including social media platforms, news pages, and any e-commerce site. Common use cases include updating the page based on information the user has entered (e.g., dynamically updating a form based on previous answers), changing the display when the user clicks a button, and providing animation. There are dozens of client-side JavaScript frameworks to choose from, including Vue, React, and Angular. More recently, it’s become possible to use JavaScript for server-side scripting, thanks to frameworks such as Node.

PHP

PHP (a recursive acronym for Hypertext Preprocessor) is very widely used for server-side scripting, powering Facebook, Wikipedia, and WordPress, to name just a few.

Server-side scripts are the instructions run on the servers hosting your website when a user visits a particular page. For example, you could use PHP scripts to call a database to retrieve availability and pricing information about a specific product or request the five most recent blog posts. The script runs when a page is requested, allowing you to populate it with the latest information without generating the page in advance.

There are a number of PHP web frameworks to choose from, including Laravel and CodeIgniter. You can also use PHP for other use cases, such as command-line scripting.

Python

One of the most popular programming languages globally, is Python. It is a scripting language with many use cases, from application and game development to data science and DevOps automation.

Python is a popular scripting choice for back-end web development, where it is used to retrieve data from databases and APIs and manipulate it for inclusion in response to the client. Python server-side web frameworks include Django, a fully-featured framework widely used for content-oriented sites including Instagram and Dropbox, and Flask, a lightweight web framework that’s popular with microservice architectures.

Perl

Perl is best known as a language for writing text-manipulation scripts, whether writing regular expressions (regex), parsing HTML, manipulating JSON files, or extracting data from log files. As a result, Perl is a popular choice for sysadmin work, such as managing file systems, databases, and users.

Thanks to both its versatility in integrating backend services and the fact it was a widely used language when the Internet started to take off, Perl was at one point a popular choice for server-side web development. 

Over the years, there have been several Perl web frameworks, with Dancer and Mojolocious being the most popular still in development. While Perl might not be your first choice when starting on a new web project, you’re likely to encounter it on older web projects, and it remains a widely used scripting language in other contexts.

Ruby

Like Python, Ruby is a general-purpose scripting language with many applications, from websites and web apps to desktop programs, data processing tools, and automated DevOps tasks.

Ruby has become increasingly popular as a server-side scripting language thanks to the Ruby on Rails web framework, which powers the likes of Airbnb, Kickstarter, and GitHub.

Wrapping up

You can think of scripting as a subset of coding, and scripting languages as a particular family of programming languages. As we’ve seen, scripting is widely used in web development to create dynamic, responsive experiences and enable pages to be generated from templates. 

But scripting is not limited to websites; you’ll find scripting languages used to create mobile and desktop apps, manipulate large data sets, automate deployments, and orchestrate machine learning utilities. In all cases, a script requires another program to run it.

Finally, as with any programming language, choosing the correct scripting language for a project will depend on several factors, including your specific use case, the existing ecosystem, and you and your team’s previous experience.

Ruby logging best practices and tips

Ruby is an opinionated language with inbuilt Ruby logging monitoring options that will serve the needs of small and basic applications. Whilst there are fewer alternatives to these than say, the JavaScript world, there are a handful, and in this post, I will highlight those that are active (based on age and commit activity) and help you figure out the options for logging your Ruby (and Rails applications).

Before proceeding, take note that this article was written using Rails v4.x; later versions of Rails may not be supported.

Best logging practices – general

Before deciding what tool works best for you, following broad logging best practices will also apply to Ruby logging and will help make anything you do log more useful when trying to track down a problem. Read “The Most Important Things to Log in Your Application Software” and the introduction of “JAVA logging – how to do it right” for more details, but in summary, these rules are:

  • Enable logging: This sounds obvious, but double check you have enabled logging (in whatever tool you use) before deploying your application and don’t solely rely on your infrastructure logging.
  • Categorize your logs: As an application grows in usage, the quantity of logs it generates will grow and the ability to filter logs to particular categories or error levels such as authorization, access, or critical can help you drill down into a barrage of information.
  • Logs are for everyone: Your logs are useful sources of information for a variety of stakeholders including support and QA engineers, and new programmers on your team. Keep them readable, understandable and with a clear purpose.

Inbuilt options

Ruby ships with two inbuilt methods for logging application flow, puts, most suited to command line applications, and logger, for larger, more complex applications.

puts takes one parameter, the object(s) you want to output to stdout, with each item outputting to a new line:

puts(@post.name, @post.title)

logger provides a lot of options for an inbuilt class, and Rails enables it by default.

logger.info #{@post.name}, #{@post.title}

The logger class provides all the log information you typically see when running Rails. You can set levels with each message, and log messages above a certain level.

require ‘logger’
logger = Logger.new(STDOUT)
logger.level = Logger::WARN

logger.debug(“No output”)
logger.info(“No output”)
logger.warn(“Output”)
logger.fatal(“Output”)

Logger doesn’t escape or sanitize output, so remember to handle that yourself. For details on how to do this, and create other forms of custom loggers and message formatters, read the official docs

Taming logger with Lograge

Whilst many Rails developers find it’s default logging options essential during development, in production, it can be noisy, overwhelming, and at worst, unhelpful. Lograge attempts to reduce this noise to more salient and useful information, and into a format that is less human-readable, but is more useful to external logging systems if you use its JSON formatted output option.

There are many ways you can initialize and configure the Gem, I stuck to the simplest, by creating a file in config/initializers/lograge.rb with the following content:

Rails.application.configure do
  config.lograge.enabled = true
end

Which changes the output to this.

ruby logging best practices

Lograge output

Unsurprisingly there are a lot of configuration options to tweak the logging output to suit you based on values available in the logging event. For example to add a timestamp:

Rails.application.configure do
  config.lograge.enabled = true

  config.lograge.custom_options = lambda do |event|
    { time: event }
  end
end

You can also add custom payloads into the logging information for accessing application controller methods such as request and current_user.

config.lograge.custom_payload do |controller|
  {
    # key_name: controller.request.*,
    # key_name: controller.current_user.*
    # etc…
  }
end

If adding all this information already feels counter to the point of lograge, then it also gives the ability to remove information based on certain criteria. For example:

config.lograge.ignore_actions = [‘PostsController#index’, ‘VisitorsController#new’]
  config.lograge.ignore_custom = lambda do |event|
    # return true if you want to ignore based on the event
  end

Logging with logging

Drawing inspiration from Java’s log4j library, logging offers similar functionality to the inbuilt logger, but adds hierarchical logging, custom level names, multiple output destinations and more.

require ‘logging’

logger = Logging.logger(STDOUT)
logger.level = :warn

logger.debug “No output”
logger.warn “output”
logger.fatal “output”

Or to create custom loggers that output to different locations and assigned to different classes.

require ‘logging’

Logging.logger[‘ImportantClass’].level = :warn
Logging.logger[‘NotSoImportantClass’].level = :debug

Logging.logger[‘ImportantClass’].add_appenders
    Logging.appenders.stdout,
    Logging.appenders.file(‘example.log’)

class ImportantClass
  logger.debug “I will log to a file”
end

class  NotSoImportantClass
  logger.debug “I will log to stdout”
end

Next Generation Logging with Semantic Logger

One of the more recent projects on this list, semantic_logger aims to support high availability applications and offers a comprehensive list of logging destinations out of the box. If you are using Rails, then instead use the rails_semantic_logger gem that overrides the Rails logger with itself. There are a lot of configuration options, where you can sort log levels, tags, log format, and much more. For example:

config.log_level = :info

# Send to Elasticsearch
SemanticLogger.add_appender(
  appender: :elasticsearch,
  url:      ‘https://localhost:9200’
)

config.log_tags = {
  #  key_name: :value,
  #  key_name:       -> request { request.object[‘value’] }
 }

Logging to external services

With all the above options you will still need to parse, process and understand your logs somehow, and numerous open source and commercial services can help you do this (open your favorite search engine and you’ll find lots), I’ll highlight those that support Ruby well.

If you’re a fluentd user, then there’s a Ruby gem that offers different ways to send your log data. If you’re a Kibana user, then Elastic offers a gem that integrates with the whole ELK stack.

Papertrail has a gem that extends the default logger to send logs to their remote endpoint. They haven’t updated it in a while, but it still their official solution, so should work, and if it doesn’t they offer an alternative method.

Loggly uses lograge and some custom configuration to send logs data to their service.

And for any Airbrake users, the company also offers a gem for direct integration into their service.

There are also a handful of gems that send the default ruby logs to syslog, which then enables you to send your logging data to a large amount of external open source and commercial logging services.

And of course, Coralogix’ own package allows you to create different loggers, assign a log level to them and other useful metadata. In addition to all standard logging features, such as flexible log querying, email alerts, centralized live tail, and a fully hosted Kibana, Coralogix provides machine learning powered anomaly detection in the context of software builds.

Another benefit is that Coralogix is the only solution which offers straightforward pricing, all packages include all features.

First, create an initializer in initializers/coralogix.rb with your account details, set a default class and extend the default Rails logger:

require ‘coralogix_logger’
PRIVATE_KEY = “<PRIVATE_KEY>”
APP_NAME = “Ruby Rails Tester”
SUB_SYSTEM = “Ruby Rails Tester Sub System”

*Private key is received upon registration, **Application name separates environments, ***Subsystem name separates components.

Coralogix::CoralogixLogger.configure(PRIVATE_KEY, APP_NAME, SUB_SYSTEM)
Rails.application.config.coralogix_logger =
Coralogix::CoralogixLogger.get_logger(“feed-service”)
Rails.logger.extend(ActiveSupport::Logger.broadcast(Rails.application.config.coralogix_logger))

And then in each class, we recommend you get an instance of the logger in each class and set the logger name to the class name, which Coralogix will use as the category. The log() method offers options to tailor the logging precision, but the severity and message are mandatory:

logger = Coralogix::CoralogixLogger.get_logger(“Posts Controller”)
logger.log(Coralogix::Severity::VERBOSE, “Post created #{post.inspect})

You can also use severity methods if you prefer, where only the message is mandatory, but other options are available:

logger.verbose(“Post created #{post.inspect})

And there you have it:

Coralogix logs stream

Know your Code

Whilst Ruby and Rails lack the ‘cool factor’ they had in the past, depending on where you look it still claims the 5th most used language (it peaked at 4th place in 2013 anyway), 12th place in the IEEE spectrum and 4th place on GitHub. It’s still a relevant, and widely used language, especially on certain platforms, such as web backends and Heroku. This means your code should be as optimized as possible. And of course, your Ruby logging/Rails logging should be as organized as possible. I hope this post will help you track down the source of potential problems in the future.