diff --git a/django/contrib/gis/gdal/__init__.py b/django/contrib/gis/gdal/__init__.py
index de41df9..c33fbcb 100644
--- a/django/contrib/gis/gdal/__init__.py
+++ b/django/contrib/gis/gdal/__init__.py
@@ -37,7 +37,7 @@
 try:
     from django.contrib.gis.gdal.driver import Driver
     from django.contrib.gis.gdal.datasource import DataSource
-    from django.contrib.gis.gdal.libgdal import gdal_version, gdal_full_version, gdal_release_date, GDAL_VERSION
+    from django.contrib.gis.gdal.libgdal import gdal_version, gdal_full_version, GDAL_VERSION
     from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
     from django.contrib.gis.gdal.geometries import OGRGeometry
     HAS_GDAL = True
diff --git a/django/contrib/gis/gdal/libgdal.py b/django/contrib/gis/gdal/libgdal.py
index 0d2889d..7c6b128 100644
--- a/django/contrib/gis/gdal/libgdal.py
+++ b/django/contrib/gis/gdal/libgdal.py
@@ -1,11 +1,16 @@
 from __future__ import unicode_literals
 
+import logging
 import os
 import re
-from ctypes import c_char_p, CDLL
+from ctypes import c_char_p, c_int, CDLL, CFUNCTYPE
 from ctypes.util import find_library
+
 from django.contrib.gis.gdal.error import OGRException
 
+
+logger = logging.getLogger('django.contrib.gis')
+
 # Custom library path set?
 try:
     from django.conf import settings
@@ -73,19 +78,6 @@ def gdal_full_version():
     "Returns the full GDAL version information."
     return _version_info('')
 
-def gdal_release_date(date=False):
-    """
-    Returns the release date in a string format, e.g, "2007/06/27".
-    If the date keyword argument is set to True, a Python datetime object
-    will be returned instead.
-    """
-    from datetime import date as date_type
-    rel = _version_info('RELEASE_DATE')
-    yy, mm, dd = map(int, (rel[0:4], rel[4:6], rel[6:8]))
-    d = date_type(yy, mm, dd)
-    if date: return d
-    else: return d.strftime('%Y/%m/%d')
-
 version_regex = re.compile(r'^(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<subminor>\d+))?')
 def gdal_version_info():
     ver = gdal_version().decode()
@@ -99,3 +91,18 @@ GDAL_MINOR_VERSION = int(_verinfo['minor'])
 GDAL_SUBMINOR_VERSION = _verinfo['subminor'] and int(_verinfo['subminor'])
 GDAL_VERSION = (GDAL_MAJOR_VERSION, GDAL_MINOR_VERSION, GDAL_SUBMINOR_VERSION)
 del _verinfo
+
+# Set library error handling so as errors are logged
+CPLErrorHandler = CFUNCTYPE(None, c_int, c_int, c_char_p)
+def err_handler(error_class, error_number, message):
+    logger.error('GDAL_ERROR %d: %s' % (error_number, message))
+err_handler = CPLErrorHandler(err_handler)
+
+def function(name, args, restype):
+    func = std_call(name)
+    func.argtypes = args
+    func.restype = restype
+    return func
+
+set_error_handler = function('CPLSetErrorHandler', [CPLErrorHandler], CPLErrorHandler)
+set_error_handler(err_handler)
diff --git a/django/contrib/gis/gdal/tests/test_ds.py b/django/contrib/gis/gdal/tests/test_ds.py
index 094f65b..69e3054 100644
--- a/django/contrib/gis/gdal/tests/test_ds.py
+++ b/django/contrib/gis/gdal/tests/test_ds.py
@@ -4,6 +4,7 @@ from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRExcept
 from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString
 from django.contrib.gis.geometry.test_data import get_ds_file, TestDS, TEST_DATA
 
+
 # List of acceptable data sources.
 ds_list = (TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile',
                   fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
@@ -59,7 +60,6 @@ class DataSourceTest(unittest.TestCase):
 
     def test03a_layers(self):
         "Testing Data Source Layers."
-        print("\nBEGIN - expecting out of range feature id error; safe to ignore.\n")
         for source in ds_list:
             ds = DataSource(source.ds)
 
@@ -108,7 +108,6 @@ class DataSourceTest(unittest.TestCase):
                         # the feature values here while in this loop.
                         for fld_name in fld_names:
                             self.assertEqual(source.field_values[fld_name][i], feat.get(fld_name))
-        print("\nEND - expecting out of range feature id error; safe to ignore.")
 
     def test03b_layer_slice(self):
         "Test indexing and slicing on Layers."
diff --git a/django/contrib/gis/gdal/tests/test_geom.py b/django/contrib/gis/gdal/tests/test_geom.py
index b22bb62..9b8ae6a 100644
--- a/django/contrib/gis/gdal/tests/test_geom.py
+++ b/django/contrib/gis/gdal/tests/test_geom.py
@@ -235,15 +235,8 @@ class OGRGeomTest(unittest.TestCase, TestDataMixin):
         # Both rings in this geometry are not closed.
         poly = OGRGeometry('POLYGON((0 0, 5 0, 5 5, 0 5), (1 1, 2 1, 2 2, 2 1))')
         self.assertEqual(8, poly.point_count)
-        print("\nBEGIN - expecting IllegalArgumentException; safe to ignore.\n")
-        try:
-            c = poly.centroid
-        except OGRException:
-            # Should raise an OGR exception, rings are not closed
-            pass
-        else:
-            self.fail('Should have raised an OGRException!')
-        print("\nEND - expecting IllegalArgumentException; safe to ignore.\n")
+        with self.assertRaises(OGRException):
+            _ = poly.centroid
 
         poly.close_rings()
         self.assertEqual(10, poly.point_count) # Two closing points should've been added
diff --git a/django/contrib/gis/geos/libgeos.py b/django/contrib/gis/geos/libgeos.py
index b31a795..d19644a 100644
--- a/django/contrib/gis/geos/libgeos.py
+++ b/django/contrib/gis/geos/libgeos.py
@@ -6,13 +6,17 @@
  This module also houses GEOS Pointer utilities, including
  get_pointer_arr(), and GEOM_PTR.
 """
+import logging
 import os
 import re
-import sys
 from ctypes import c_char_p, Structure, CDLL, CFUNCTYPE, POINTER
 from ctypes.util import find_library
+
 from django.contrib.gis.geos.error import GEOSException
 
+
+logger = logging.getLogger('django.contrib.gis')
+
 # Custom library path set?
 try:
     from django.conf import settings
@@ -56,23 +60,23 @@ lgeos = CDLL(lib_path)
 # Supposed to mimic the GEOS message handler (C below):
 #  typedef void (*GEOSMessageHandler)(const char *fmt, ...);
 NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
-def notice_h(fmt, lst, output_h=sys.stdout):
+def notice_h(fmt, lst):
     fmt, lst = fmt.decode(), lst.decode()
     try:
         warn_msg = fmt % lst
     except:
         warn_msg = fmt
-    output_h.write('GEOS_NOTICE: %s\n' % warn_msg)
+    logger.warn('GEOS_NOTICE: %s\n' % warn_msg)
 notice_h = NOTICEFUNC(notice_h)
 
 ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
-def error_h(fmt, lst, output_h=sys.stderr):
+def error_h(fmt, lst):
     fmt, lst = fmt.decode(), lst.decode()
     try:
         err_msg = fmt % lst
     except:
         err_msg = fmt
-    output_h.write('GEOS_ERROR: %s\n' % err_msg)
+    logger.error('GEOS_ERROR: %s\n' % err_msg)
 error_h = ERRORFUNC(error_h)
 
 #### GEOS Geometry C data structures, and utility functions. ####
diff --git a/django/contrib/gis/geos/tests/test_geos.py b/django/contrib/gis/geos/tests/test_geos.py
index cbe5136..283daa4 100644
--- a/django/contrib/gis/geos/tests/test_geos.py
+++ b/django/contrib/gis/geos/tests/test_geos.py
@@ -147,18 +147,13 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
     def test_errors(self):
         "Testing the Error handlers."
         # string-based
-        print("\nBEGIN - expecting GEOS_ERROR; safe to ignore.\n")
         for err in self.geometries.errors:
-            try:
-                g = fromstr(err.wkt)
-            except (GEOSException, ValueError):
-                pass
+            with self.assertRaises((GEOSException, ValueError)):
+                _ = fromstr(err.wkt)
 
         # Bad WKB
         self.assertRaises(GEOSException, GEOSGeometry, memoryview(b'0'))
 
-        print("\nEND - expecting GEOS_ERROR; safe to ignore.\n")
-
         class NotAGeometry(object):
             pass
 
@@ -458,7 +453,6 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
 
     def test_multipolygons(self):
         "Testing MultiPolygon objects."
-        print("\nBEGIN - expecting GEOS_NOTICE; safe to ignore.\n")
         prev = fromstr('POINT (0 0)')
         for mp in self.geometries.multipolygons:
             mpoly = fromstr(mp.wkt)
@@ -477,8 +471,6 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
                     self.assertEqual(p.valid, True)
                 self.assertEqual(mpoly.wkt, MultiPolygon(*tuple(poly.clone() for poly in mpoly)).wkt)
 
-        print("\nEND - expecting GEOS_NOTICE; safe to ignore.\n")
-
     def test_memory_hijinks(self):
         "Testing Geometry __del__() on rings and polygons."
         #### Memory issues with rings and polygons
@@ -1025,19 +1017,15 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
 
         g = GEOSGeometry("POINT(0 0)")
         self.assertTrue(g.valid)
-        self.assertTrue(isinstance(g.valid_reason, six.string_types))
+        self.assertIsInstance(g.valid_reason, six.string_types)
         self.assertEqual(g.valid_reason, "Valid Geometry")
 
-        print("\nBEGIN - expecting GEOS_NOTICE; safe to ignore.\n")
-
         g = GEOSGeometry("LINESTRING(0 0, 0 0)")
 
-        self.assertTrue(not g.valid)
-        self.assertTrue(isinstance(g.valid_reason, six.string_types))
+        self.assertFalse(g.valid)
+        self.assertIsInstance(g.valid_reason, six.string_types)
         self.assertTrue(g.valid_reason.startswith("Too few points in geometry component"))
 
-        print("\nEND - expecting GEOS_NOTICE; safe to ignore.\n")
-
     @unittest.skipUnless(geos_version_info()['version'] >= '3.2.0', "geos >= 3.2.0 is required")
     def test_linearref(self):
         "Testing linear referencing"
